Ruby 浮点精度

Ruby float precision

提问人: 提问时间:9/6/2011 最后编辑:mskfisher 更新时间:5/10/2012 访问量:13543

问:

据我了解,Ruby (1.9.2) 浮点数的精度为 15 位十进制数字。因此,我希望将浮点数 x 四舍五入到小数点后 15 位等于 x。对于此计算,情况并非如此。

x = (0.33 * 10)
x == x.round(15) # => false

顺便说一句,四舍五入到 16 位将返回 true。

你能向我解释一下吗?

Ruby 浮点 精度

评论

1赞 Pascal Cuoq 9/6/2011
您的语言很可能在内部使用 IEEE 754 双精度浮点数。“15 位十进制数字”只是一个近似值,因为浮点数实际上是以 2 为基数表示的,而不是以 10 为基数。
0赞 fl00r 9/6/2011
浮点数是如此浮动:)
1赞 Andrew Grimm 9/6/2011
一个我不想关闭锤子的关于浮动精度的问题!+1.000000000001!
1赞 Reactormonk 5/10/2012
我想知道为什么你可以浮动数字,这是一个陷阱。==

答:

2赞 Matchu 9/6/2011 #1

好吧,虽然我不确定 Ruby 如何在内部处理浮点数的细节,但我确实知道为什么这部分代码在我的盒子上失败了:

 > x = (0.33 * 10)
=> 3.3000000000000003
 > x.round(15)
=> 3.300000000000001

无论出于何种原因,第一个浮点数保留 16 位小数,总共 17 位。因此,四舍五入到 15 会丢弃这些数字。

评论

0赞 9/6/2011
为什么 3.30000000000000003.round(15) == 3.3000000000000001 而不是 3.3?这是否仅归因于二进制表示和浮点舍入误差?
2赞 Rudy Velthuis 9/6/2011
@georgehemmings:是的,它确实归结为 0.1(或它的倍数)的二进制表示不是 1 / 2^n 项的有限级数。
10赞 DigitalRoss 9/6/2011 #2

部分问题在于 0.33 在基础格式中没有精确的表示,因为它不能用一系列 1 / 2n 项来表示。因此,当它乘以 10 时,一个与 0.33 略有不同的数字被乘以。

就此而言,3.3 也没有确切的表示。

第一部分

当数字没有精确的以 10 为基数表示时,在转换尾数中有信息的最低有效数字时会有余数。这些剩余部分将向右传播,可能永远传播,但这在很大程度上毫无意义。这个错误的明显随机性是由于相同的原因,解释了你和 Matchu 注意到的明显不一致的四舍五入。这是第二部分。

第二部分

并且这些信息(最右边的位)与单个十进制数字传达的信息没有整齐地对齐,因此十进制数字通常会比原始精度更高时其值略小。

这就是为什么转换可能在 15 位时四舍五入为 1,在 16 位时四舍五入为 0.x:因为较长的转换对尾数末尾右侧的位没有值。

评论

0赞 9/6/2011
感谢您到目前为止的帮助。我明白了大致的想法,但我仍在努力向自己证明这一点。如果 Ruby 只保留大约 15 位有效数字,为什么 sprintf(“%.50f”,1.1) 在小数点后第 15 位之后打印非零?这些价值观从何而来?
0赞 Matchu 9/6/2011
+1 耶,有人知道细节!Ruby 愿意在被强制显示其他小数位时显示其他小数位,这一事实似乎表明它四舍五入了一些小数位,并且实际上将其他信息保留在默认显示的范围之外。to_s
0赞 DigitalRoss 9/7/2011
乔治。。。转换的工作方式是将值除以,然后将余数转换为更右边的数字。因此,除非这恰好为零,否则在尾数位用完后,转换可能会在相当长的一段时间内产生非零位数。它可以永远持续下去,重复商数。我想有人可能会想出一种算法来故意逐渐减少到零,但请注意,如此转换的数字将比具有尾随余数位的数字更远。最好不要首先转换多余的数字。
0赞 DigitalRoss 9/7/2011
我还应该补充一点,这个问题也发生在无理数和确实比尾数可以指定的精度更高的数字上。
0赞 9/9/2011
谢谢,我现在明白了。我决定看看 C# 是做什么的;它拒绝显示超过 17 位小数,即使被要求提供 50 位。对于 1.1,它只显示 16 位小数。(1.1D)。ToString(“G50”)。长度 == 18