浅谈浮点数精度中的误差

    浮点型数据是表示具有小数点的实数的。为什么在C中把实数称为浮点数呢?在C语言中,实数是以指数形式存放在存储单元中的。我们都知道实数是一个连续的无穷集合,即使在0—1这个区间也会有无穷个数,计算机会把1.0000000000001默认为1,也会把0.99999999999默认为1,这样,就会出现误差。
   在数学中,我们常常会看到“误差为1m”“误差范围”这样的字眼。比如:官方公布A、B两地距离为200m,存在1m误差。但是测量数据出现199.9,189.9,201.1这样的数据,显然,只有199.9这组数据更符合实际,因为存在1m误差的合理范围为[199,201],而189.9,201.1都不在这个范围内。因此我们可以做出这种总结:设真实值为x,给出值为y,误差为r;则:y-r<=x<=y+r。

在编程过程中,我们常常会遇到一个变量和一个准确的数进行比较,最经典的就是判断一个数和0的大小。比如说:a等于0,我们会直接写成 a==0,但在认识到精确度问题后,我们就很自然的考虑误差问题。对此,我们不妨定义一个宏,设置其误差范围。以一元二次方程ax^2+bx+c=0求解为例:
#define EPS 0.000001
void Fun(double a,double b,double c)
{
    double x1;
    double x2;
    double d=b*b-4*a*c;
    if(0-EPS<=a&&a<=EPS)//判断a==0
    {
        x1=x2=-c/b;
	printf("x1=%f,x2=%f\n",x1,x2);
    }
    else if(0-EPS<=d&&d<=EPS)//判断d==0
    {
	x1=x2=-b/(2*a);
	printf("x1=%f,x2=%f\n",x1,x2);
    }
    else if(d>EPS)//判断d>0
    {
	x1=(-b+sqrt(d)/(2*a));
	x2=(-b-sqrt(d)/(2*a));
	printf("x1=%f,x2=%f\n",x1,x2);
    }
    else
    {
	printf("无实根!\n");
    }
}

<think>好的,我现在需要帮用户理解MySQL中浮点数精度误差的原理。用户之前已经询问过MySQL浮点数的使用指南,现在他们想更深入地了解精度误差的原理。首先,我得回顾一下之前的回答,确保这次的内容不重复但能衔接上。 根据用户提供的引用内容,引用[1]提到浮点数在存储时使用二进制近似值,比如0.1存储为0.10000000149011612,这和IEEE 754标准有关。引用[3]解释了浮点数为何不适合精确计算,因为它们用科学计数法存储,导致精度问题。引用[4]则比较了不同浮点类型的应用场景。 用户现在的问题是关于原理,所以需要从计算机存储浮点数的方式入手。首先,二进制表示的问题,例如十进制0.1无法精确转换为二进制,这会引发误差。接着是IEEE 754标准,这个标准规定了单精度和双精度浮点数的存储格式,包括符号位、指数位和尾数位。FLOAT是32位,DOUBLE是64位,它们的精度有限,导致舍入误差。 然后要考虑科学计数法的存储方式,浮点数用尾数和指数表示,这种动态范围虽然适合科学计算,但牺牲了精确性。比如,当数值超出尾数能表示的范围时,就会发生舍入。此外,运算过程中累积的误差,比如多次运算后的误差叠加,也会影响结果。 还要结合用户之前的问题,他们在使用指南中提到了DECIMAL类型作为解决方案,这里需要对比说明为什么DECIMAL能避免这些问题,因为它用字符串存储数字,没有二进制转换的问题。 最后,可能需要给出实际例子,比如0.1在FLOAT中的存储情况,以及如何通过查询验证误差的存在。同时,用户可能还想知道如何避免这些误差,所以需要简要提到使用DECIMAL或比较时用范围而非等值判断。 需要确保回答结构清晰,分点解释原理,并引用用户提供的引用内容来支持观点。同时,保持语言通俗易懂,避免过于技术化的术语,但又要准确。</think>#### MySQL浮点数精度误差原理详解 MySQL浮点数精度误差的核心源于**二进制浮点数表示与十进制转换的不可调和矛盾**,其本质由IEEE 754浮点数标准和计算机存储机制共同决定。以下是原理的逐层解析: --- ### 一、二进制表示陷阱 1. **非精确映射** - **十进制小数无法完全转换为二进制**:例如十进制$0.1$转换为二进制是无限循环小数$0.0001100110011..._2$,类似$1/3$在十进制中无法精确表示 - **存储截断**:MySQL的$FLOAT$(32位)和$DOUBLE$(64位)必须将无限循环小数截断为有限位存储,产生固有误差[^1] - **验证示例**: ```sql CREATE TABLE test (f FLOAT); INSERT INTO test VALUES (0.1); SELECT f, HEX(f) FROM test; -- 输出0.1,但实际存储为0x3DCCCCCD(对应0.10000000149011612) ``` 2. **IEEE 754标准限制** - **存储结构**: $$ \text{浮点数} = (-1)^s \times m \times 2^e $$ 其中$s$为符号位,$m$为尾数(mantissa),$e$为指数(exponent)[^3] - **精度天花板**: | 类型 | 尾数有效位(二进制) | 十进制精度上限 | |--------|---------------------|---------------| | FLOAT | 23 bits | 约7位有效数字 | | DOUBLE | 52 bits | 约15位有效数字 | --- ### 二、科学计数法的动态范围代价 1. **动态范围与精度取舍** - **指数位控制范围**:$FLOAT$指数8位(范围$±10^{38}$),$DOUBLE$指数11位(范围$±10^{308}$) - **尾数位决定精度**:随着数值增大,相邻可表示数的间隔呈指数增长 - **示例问题**: ```sql SELECT (100000000.1 + 0.1) = 100000000.2; -- 返回0(False) -- 因大数区尾数间隔超过0.1,加法操作无法正确反映变化 ``` 2. **累积误差放大效应** - **运算链式反应**:每次浮点运算都会引入新误差,且误差会随运算次数增加被放大 - **典型场景**: ```sql CREATE TABLE acc (amount FLOAT); INSERT INTO acc VALUES (0); -- 循环执行100次:UPDATE acc SET amount = amount + 0.1; SELECT amount FROM acc; -- 理论值10.0,实际输出9.999999776482582 ``` --- ### 三、MySQL的特定实现机制 1. **存储与计算分离** - **存储阶段**:输入值被强制转换为最接近的可表示浮点值 - **计算阶段**:所有运算使用二进制浮点运算规则 - **显示阶段**:MySQL客户端自动进行十进制舍入显示(隐藏实际存储值)[^2] 2. **隐式转换风险** - **类型混用陷阱**:当浮点字段与字符串/定点数比较时,MySQL会优先将非浮点数据转为浮点数: ```sql SELECT * FROM products WHERE price = '19.99'; -- '19.99'被转为19.99000065612793 ``` --- ### 四、与DECIMAL的对比验证 通过对比实验可直观理解差异: ```sql CREATE TABLE compare ( f FLOAT, d DECIMAL(10,2) ); INSERT INTO compare VALUES (0.1, 0.1), (0.1+0.1+0.1, 0.3); SELECT f AS float_val, d AS decimal_val, f = 0.3 AS float_equals, d = 0.3 AS decimal_equals FROM compare; -- 输出结果: -- float_val | decimal_val | float_equals | decimal_equals -- 0.1 | 0.10 | 0 | 1 -- 0.3 | 0.30 | 0 | 1 <-- FLOAT实际存储为0.30000001192092896 ``` --- ### 五、工程实践启示 1. **误差不可避免性** - 浮点数精度误差是计算机体系结构的数学特性决定,与数据库实现无关 2. **误差控制方法论** - **绝对误差阈值法**:`WHERE ABS(a - b) < 1e-6` - **整数放大法**:将金额存储为分(乘以100后转整型) - **UDF扩展法**:通过MySQL用户定义函数实现高精度计算 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值