提问人:MrDatabase 提问时间:10/30/2008 最后编辑:MrDatabase 更新时间:3/25/2022 访问量:49362
什么是浮点/舍入误差的简单示例?
What is a simple example of floating point/rounding error?
问:
我听说过使用浮点变量时的“错误”。现在我正在尝试解决这个难题,我想我得到了一些舍入/浮点错误。因此,我终于要弄清楚浮点误差的基础知识了。
浮点/舍入错误的简单示例是什么(最好在 C++ 中)?
编辑:例如,假设我有一个事件,其成功的概率为p。我做了这个事件 10 次(p 没有改变,所有试验都是独立的)。2 次试验成功的概率是多少?我将其编码为:
double p_2x_success = pow(1-p, (double)8) * pow(p, (double)2) * (double)choose(8, 2);
这是浮点误差的机会吗?
答:
通常,浮点误差是指当一个数字不能存储在IEEE浮点表示中时。
存储整数时,最右边的位是 1,左边的每个位是 (2,4,8,...) 的两倍。很容易看出,它可以存储最多 2^n 的任何整数,其中 n 是位数。
浮点数的尾数(小数部分)以类似的方式存储,但从左向右移动,每个连续位都是前一个位值的一半。(它实际上比这要复杂一些,但现在可以)。
因此,像 0.5 (1/2) 这样的数字很容易存储,但并不是每个数字 <1 都可以通过添加固定数量的 1/2、1/4、1/8、...
一个非常简单的例子是 0.1,即 1/10。这可以用一个无穷级数来完成(我真的懒得计算),但是每当计算机存储 0.1 时,存储的就不完全是这个数字。
如果你可以访问 Unix 机器,很容易看到这一点:
Python 2.5.1 (r251:54863, Apr 15 2008, 22:57:26)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.1
0.10000000000000001
>>>
无论您使用哪种语言,您都需要非常小心地进行浮点数和双精度的相等性测试。
(至于你的例子,0.2 是另一个不能存储在 IEEE 二进制文件中的讨厌的数字,但只要你测试不等式,而不是等式,比如 p <= 0.2,那么你就会没问题。
不久前,一个简单的 C 语言示例引起了我的注意:
double d = 0;
sscanf("90.1000", "%lf", &d);
printf("%0.4f", d);
这将打印90.0999
这是在将 DMS 中的角度转换为弧度的函数中。
为什么在上述情况下不起作用?
评论
sscanf
float
double
double
printf
sscanf
double
for(double d = 0; d != 0.3; d += 0.1); // never terminates
图片胜过千言万语 - 尝试绘制方程式:
您将得到这样的 XY 图(X 和 Y 为对数刻度)。
如果计算机可以表示 32 位浮点数而没有舍入误差,那么对于每一个浮点数,我们应该得到零。但是,由于浮点误差累积,误差会随着 k 值的增加而增加。f(k)
k
呵呵!
评论
(iter = 10**7, Math.abs((iter*0.1)**2 - Array(iter).fill(0.1).reduce((x,a) => a+x)**2));
这里有一个抓住了我:
round(256.49999) == 256
roundf(256.49999) == 257
双精度和浮点数具有不同的精度,因此第一个将表示为 ,第二个表示为 ,因此舍入方式不同256.49999000000003
256.5
我喜欢 Python 解释器的这个:
Python 2.7.10 (default, Oct 6 2017, 22:29:07)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> 0.1+0.2
0.30000000000000004
>>>
超级简单(Python):
a = 10000000.1
b = 1/10
print(a - b == 10000000)
print ('a:{0:.20f}\nb:{1:.20f}'.format(a, b))
打印(取决于平台)如下:
False
a:10000000.09999999962747097015
b:0.10000000000000000555
评论
1/10
0.00000
)
这是我想到的最简单的,应该适用于多种语言,很简单:
0.2 + 0.1
以下是我想到的 REPL 的一些示例,但应该在任何符合 IEEE754 的语言上返回此结果。
蟒
>>> 0.2 + 0.1
0.30000000000000004
Kotlin
0.2 + 0.1
res0: kotlin.Double = 0.30000000000000004
斯卡拉
scala> 0.2 + 0.1
val res0: Double = 0.30000000000000004
爪哇岛
jshell> 0.2 + 0.1
$1 ==> 0.30000000000000004
红宝石
irb(main):001:0> 0.2 + 0.1
=> 0.30000000000000004
评论
0.3
我认为 Ruby 在其文档中有一个很好的例子:
sum = 0
10_000.times do
sum = sum + 0.0001
end
print sum #=> 0.9999999999999062
评论