为什么BigDecimal会返回一个奇怪的值?

Til*_*dor 15 ruby floating-point bigdecimal

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

这个说法:

1876.8 == BigDecimal('1876.8')
Run Code Online (Sandbox Code Playgroud)

返回false.

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

"%.20f" % 1876.8 => 1876.8000000000000
"%.20f" % BigDecimal('1876.8') => 1876.8000000000002
Run Code Online (Sandbox Code Playgroud)

请注意2最后一个小数位的BigDecimal中的额外值.

我认为BigDecimal应该能够抵消直接在计算机的本机浮点中存储实数的不准确性.这是2从哪里来的?

Jas*_*rue 9

它不会给你那么多的小数位数控制,但BigDecimal的传统格式机制似乎是:

a.to_s('F')
Run Code Online (Sandbox Code Playgroud)

如果您需要更多控制,请考虑使用Money gem,假设您的域名问题主要与货币有关.

gem install money
Run Code Online (Sandbox Code Playgroud)


Dav*_*vid 8

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

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

无论哪种方式,您都不可能获得将float与BigDecimal进行比较的准确结果.


Dig*_*oss 7

不要比较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 n,但十进制常数是x /(2 n*5 m).您作为符号,指数和尾数的值是无限重复的0 10000001001 1101010100110011001100110011001100110011001100110011...具有讽刺意味的是,FP算术非常精确,并且当值没有分数时,相等比较可以很好地工作.