为什么ruby BigDecimal显示类似于float的表示不准确?

kdb*_*man 2 ruby floating-point precision bigdecimal

某些浮点数具有二进制浮点表示的固有不准确性:

> puts "%.50f" % (0.5)  # cleanly representable
0.50000000000000000000000000000000000000000000000000

> puts "%.50f" % (0.1)  # not cleanly representable
0.10000000000000000555111512312578270211815834045410
Run Code Online (Sandbox Code Playgroud)

这不是什么 新鲜事.但为什么红宝石BigDecimal也表现出这种行为呢?

> puts "%.50f" % ("0.1".to_d)
0.10000000000000000555111512312578270211815834045410
Run Code Online (Sandbox Code Playgroud)

(我正在使用rails速记.to_dBigDecimal.new不仅仅是为了简洁,这不是特定于rails的问题.)

问题:为什么"0.1".to_d仍然会出现10 -17的错误?我认为目的BigDecimal明确是为了避免这样的不准确?

起初我认为这是因为我将一个已经不准确的浮点转换0.1BigDecimal,并且BigDecimal只是无损地表示不准确.但我确保我使用的是字符串构造函数(如上面的代码片段所示),这应该可以避免这个问题.


编辑:

更多的调查表明,BigDecimal内部仍然干净利落.(很明显,因为否则这将是一个非常广泛使用的系统中的一个巨大的错误.)这是一个仍然显示错误的操作示例:

> puts "%.50f" % ("0.1".to_d * "10".to_d)
1.00000000000000000000000000000000000000000000000000
Run Code Online (Sandbox Code Playgroud)

如果表示是有损的,那将显示与上面相同的误差,只是移动了一个数量级.这里发生了什么?

tad*_*man 5

%.50f说明符需要一个浮点值,从而使十进制值需要转换为浮点之前呈现的显示,并因此受到相同的浮点噪音你在普通的浮动点值获得.

sprintf和朋友一样String#%,根据占位符中指定的类型自动进行转换.

为了抑制你必须直接在BigDecimal数上使用该.to_s方法.如果您需要一定数量的位置,它可以采用可选的格式说明符,并且可以将其链接到%s另一个字符串中的占位符.