我正在编写处理货币、费用等的代码。我将使用 BigDecimal 类进行数学和存储,但我们遇到了一些奇怪的问题。

这个说法:

1876.8 == BigDecimal('1876.8')

返回假。

如果我通过格式化字符串运行这些值 "%.13f" 我得到:

"%.20f" % 1876.8 => 1876.8000000000000
"%.20f" % BigDecimal('1876.8') => 1876.8000000000002

注意额外的 2 从 BigDecimal 的最后一位小数开始。

我认为 BigDecimal 应该解决直接在计算机的本机浮点中存储实数的不准确性。这是哪里 2 来自(哪里?

有帮助吗?

解决方案

你是对的,BigDecimal 应该正确存储它,我最好的猜测是:

  • BigDecimal 正确存储值
  • 当传递给字符串格式化函数时,BigDecimal 将被转换为较低精度的浮点值,从而创建 ...02。
  • 当直接与浮点数比较时,浮点数有一个额外的小数位,远远超出了您看到的 20(经典浮点数无法进行比较行为)。

无论哪种方式,将浮点数与 BigDecimal 进行比较都不太可能获得准确的结果。

其他提示

这不会给你在小数位的数量一样多的控制,但传统的格式机制的BigDecimal似乎是:

a.to_s('F')

如果您需要更多的控制,可考虑使用货币宝石,假设您正在访问的问题主要是关于货币。

gem install money

不相等比较FPU十进制字符串馏分

的问题是,一个浮动或双值的与包含一小部分的十进制常数相等比较很少成功。

很少十进制字符串馏分具有在二进制表示FP的精确值,所以相等比较通常注定。 *

要回答您的确切问题,2是从稍微不同的转换的十进制字符串馏分的进Float格式的到来。由于分数不能精确表示,它可能是两次计算将考虑在中间计算不同量的精度,并最终结束了的结果不同的舍入到52位IEEE 754双精度尾数。这并不重要,因为那里的的没有确切的表示,无论如何,而是一个可能比另一个更错了。

在特别是,此1876.8不能准确地由FP对象表示,事实上,在0.01和0.99,只有0.25,0.50,和0.75有精确的二进制表示。所有的人,包括1876.8,重复永远四舍五入至52位。这是一半左右的BigDecimal甚至存在的原因。 (的原因,另一半是FP数据的固定精度:有时你需要更多)

所以,你有一个十进制字符串常量比较实际机器价值时,你得到的结果依赖于二进制小数每一个位...下降为1/2 52 ...和即使在那时需要舍入。

如果有什么甚至半点(和合,位,对不起)不完善关于所产生的数量,或输入转换代码,或任何其他涉及的过程中,他们不会看正好等于

这是参数甚至可以被做出的比较总是失败,因为没有IEEE-格式FPU甚至可以代表号码完全。他们真的是不相等的,即使他们看起来像它。在左边,你的十进制字符串已转换为二进制字符串,而大部分的数字只是不完全转换。在右边,它仍然是一个十进制字符串。

所以,不要用BigDecimal的混合浮动,只是比较一个BigDecimal与其它的BigDecimal。 (即使两个操作数的的花车,测试平等,需要特别小心或模糊测试还有,不要相信每一个格式化的数字:输出格式将携余路要走分数的右侧,所以你一般不会开始看到零,你只会看到垃圾值。)


* 问题:机器号为x / 2 名词,但十进制常数分别是x /(2 名词 * 5 )。你作为符号,指数和尾数值讽刺的是无限重复0 10000001001 1101010100110011001100110011001100110011001100110011...,FP算术是完全精确和相等比较很好地工作时的值没有分数。

如大卫说,BigDecimal的正存储它的权利

 p (BigDecimal('1876.8') * 100000000000000).to_i

返回187680000000000000

所以,是的,字符串格式化是破坏它

如果你不需要分数美分,考虑存储和操作的货币为一个整数,然后除以100当它的时间来显示。我觉得这不是处理存储和浮点操作的必然精度问题变得更加容易。

在Mac OS X上,我跑ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9]

irb(main):004:0> 1876.8 == BigDecimal('1876.8') => true

然而,作为Ruby的,我想你应该考虑在发送对象的消息的条款。这是什么对您的回报:

BigDecimal('1876.8') == 1876.8

两者是不等同的,如果你正在尝试使用BigDecimal的能力,以确定精确的小数点平等,应该是消息询问平等的接收器。

出于同样的原因,我不认为通过发送格式消息的格式字符串格式化BigDecimal为正确的方法无论是。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top