【发布时间】:2009-09-20 13:26:18
【问题描述】:
为什么 ruby 中的这段代码 7.30 - 7.20 返回 0.0999999999999996,而不是 0.10?
但是如果我写7.30 - 7.16,例如,一切都会好的,我会得到0.14。
什么问题,我该如何解决?
【问题讨论】:
标签: ruby math floating-point ieee-754
为什么 ruby 中的这段代码 7.30 - 7.20 返回 0.0999999999999996,而不是 0.10?
但是如果我写7.30 - 7.16,例如,一切都会好的,我会得到0.14。
什么问题,我该如何解决?
【问题讨论】:
标签: ruby math floating-point ieee-754
【讨论】:
问题是我们可以很容易地用十进制写的一些数字在当前硬件实现的特定浮点格式中没有精确的表示。一种随意的说法是所有整数都有,但不是所有的分数,因为我们通常用2**e 指数存储分数。因此,您有 3 个选择:
适当四舍五入。未四舍五入的结果总是非常接近,因此四舍五入的结果总是“完美”的。这就是 Javascript 所做的事情,很多人甚至没有意识到 JS 在浮点中做所有事情。
使用定点算法。 Ruby 实际上使这变得非常容易。随着数字变大,它是唯一一种从 Fixnum 无缝转换到 Bignum 类的语言。
使用旨在解决此问题的类,例如BigDecimal
要更详细地查看问题,我们可以尝试用二进制表示您的“7.3”。 7 部分很简单,111,但我们如何做 0.3? 111.1是7.5,太大了,111.01是7.25,越来越近了。事实证明,111.010011 是“下一个最接近的较小数字”,7.296875,当我们尝试填充缺失的 .003125 时,最终我们发现它只是 111.010011001100110011……永远,在我们选择的有限位字符串编码中无法表示.
【讨论】:
问题在于floating point is inaccurate。您可以使用 Rational、BigDecimal 或仅使用普通整数来解决它(例如,如果您想存储钱,您可以将美分数存储为 int 而不是将美元数存储为浮点数)。
BigDecimal 可以准确地存储任何以 10 为底的有限位数的数字,并对没有的数字进行四舍五入(因此三分之三不是整数)。
有理数可以准确存储任何有理数,完全不能存储无理数。
【讨论】:
这是浮点数在内存中表示方式的常见错误。
如果您需要精确的结果,请使用 BigDecimal。
result=BigDecimal.new("7.3")-BigDecimal("7.2")
puts "%2.2f" % result
【讨论】:
有趣的是,一个基数中小数位数很少的数字通常在另一个基数中可能有大量小数。例如,以 10 为底数表示 1/3 (=0.3333...) 需要无限位小数,但以 3 为底数只需要一位小数。类似地,表示数字 1/10 ( =0.1) 在基数 2.
【讨论】:
由于您正在进行浮点数学运算,因此返回的数字就是您的计算机用于精度的数字。
如果您想要更接近的答案,达到设定的精度,只需将浮点数乘以该值(例如乘以 100),将其转换为 int,进行数学运算,然后除。
还有其他解决方案,但我发现这是最简单的,因为舍入对我来说总是有点不确定。
这里之前有人问过这个问题,你可能想找一些之前给出的答案,比如这个: Dealing with accuracy problems in floating-point numbers
【讨论】: