转换为科学计数法时出现双精度错误
double precision error when converting to scientific notation
我正在构建一个程序,将双精度值转换为科学值格式(尾数、指数)。然后我注意到下面
369.7900000000000 -> 3.6978999999999997428
68600000 -> 6.8599999999999994316
我注意到其他几个值也有相同的模式。最大小数误差为
0.000 000 000 000 001 = 1*e-15
我知道在计算机中表示双精度值的不准确性。这是否可以得出结论,我们将得到的最大小数误差是 1*e-15
?这有什么重要意义?
大部分关于栈溢出中浮点精度问题的题我都看完了,但是我没有看到任何关于64位最大小数误差的问题。
为了清楚我所做的计算,我也提到了我的代码片段
double norm = 68600000;
if (norm)
{
while (norm >= 10.0)
{
norm /= 10.0;
exp++;
}
while (norm < 1.0)
{
norm *= 10.0;
exp--;
}
}
现在我明白了
norm = 6.8599999999999994316;
exp = 7
您获得的数字与 double
数据类型的 machine epsilon 有关。
一个double
是64位长,1位符号,11位指数,52位尾数。 double
的值由
给出
1.mmmmm... * (2^exp)
由于尾数只有 52 位,任何低于 2^-52
的 double
值在添加到 1.0
时将完全丢失,因为它意义不大。在二进制中,1.0 + 2^-52
将是
1.000...00 + 0.000...01 = 1.000.....01
显然任何更低的值都不会改变 1.0
的值。你可以在程序中自己验证1.0 + 2^-53 == 1.0
。
这个数字 2^-52 = 2.22e-16
称为 机器 epsilon 并且是在一次浮点运算期间由于舍入误差而发生的相对误差的上限double
个值。
类似地,float
在其尾数中有 23 位,因此其机器 epsilon 为 2^-23 = 1.19e-7
。
你得到 1e-15
的原因可能是因为你执行许多算术运算时错误会累积,但我不能说,因为我不知道你正在做的确切计算。
编辑:我已经调查了您的 68600000 问题的相对错误。
首先,您可能有兴趣知道 round-off 如果您将计算分成几步,错误会改变您的计算结果:
686.0/10.0 = 68.59999999999999431566
686.0/10.0/10.0 = 6.85999999999999943157
686.0/100.0 = 6.86000000000000031974
在第一行中,最接近68.6的double
低于实际值,但在第三行中我们看到最接近6.86的double
更大。
如果我们查看您程序的绝对错误 e_abs = abs(v-v_approx)
,我们会发现它是
6.8600000 - 6.85999999999999943156581139192 ~= 5.684e-16
然而,相对误差 e_abs = abs( (v-v_approx)/ v) = abs(e_abs/v)
将是
5.684e-16 / 6.86 ~= 8.286e-17
这确实低于我们 2.22e-16
的机器 epsilon。
This 是一篇著名的论文,如果您想了解有关浮点运算的所有细节,可以阅读。
我正在构建一个程序,将双精度值转换为科学值格式(尾数、指数)。然后我注意到下面
369.7900000000000 -> 3.6978999999999997428
68600000 -> 6.8599999999999994316
我注意到其他几个值也有相同的模式。最大小数误差为
0.000 000 000 000 001 = 1*e-15
我知道在计算机中表示双精度值的不准确性。这是否可以得出结论,我们将得到的最大小数误差是 1*e-15
?这有什么重要意义?
大部分关于栈溢出中浮点精度问题的题我都看完了,但是我没有看到任何关于64位最大小数误差的问题。
为了清楚我所做的计算,我也提到了我的代码片段
double norm = 68600000;
if (norm)
{
while (norm >= 10.0)
{
norm /= 10.0;
exp++;
}
while (norm < 1.0)
{
norm *= 10.0;
exp--;
}
}
现在我明白了
norm = 6.8599999999999994316;
exp = 7
您获得的数字与 double
数据类型的 machine epsilon 有关。
一个double
是64位长,1位符号,11位指数,52位尾数。 double
的值由
1.mmmmm... * (2^exp)
由于尾数只有 52 位,任何低于 2^-52
的 double
值在添加到 1.0
时将完全丢失,因为它意义不大。在二进制中,1.0 + 2^-52
将是
1.000...00 + 0.000...01 = 1.000.....01
显然任何更低的值都不会改变 1.0
的值。你可以在程序中自己验证1.0 + 2^-53 == 1.0
。
这个数字 2^-52 = 2.22e-16
称为 机器 epsilon 并且是在一次浮点运算期间由于舍入误差而发生的相对误差的上限double
个值。
类似地,float
在其尾数中有 23 位,因此其机器 epsilon 为 2^-23 = 1.19e-7
。
你得到 1e-15
的原因可能是因为你执行许多算术运算时错误会累积,但我不能说,因为我不知道你正在做的确切计算。
编辑:我已经调查了您的 68600000 问题的相对错误。
首先,您可能有兴趣知道 round-off 如果您将计算分成几步,错误会改变您的计算结果:
686.0/10.0 = 68.59999999999999431566
686.0/10.0/10.0 = 6.85999999999999943157
686.0/100.0 = 6.86000000000000031974
在第一行中,最接近68.6的double
低于实际值,但在第三行中我们看到最接近6.86的double
更大。
如果我们查看您程序的绝对错误 e_abs = abs(v-v_approx)
,我们会发现它是
6.8600000 - 6.85999999999999943156581139192 ~= 5.684e-16
然而,相对误差 e_abs = abs( (v-v_approx)/ v) = abs(e_abs/v)
将是
5.684e-16 / 6.86 ~= 8.286e-17
这确实低于我们 2.22e-16
的机器 epsilon。
This 是一篇著名的论文,如果您想了解有关浮点运算的所有细节,可以阅读。