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

Why does ruby BigDecimal show representation inaccuracy similar to float?

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

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

> puts "%.50f" % (0.1)  # not cleanly representable
0.10000000000000000555111512312578270211815834045410

这是nothing new. But why does ruby's BigDecimal也表现出这种行为?

> puts "%.50f" % ("0.1".to_d)
0.10000000000000000555111512312578270211815834045410

(我使用 rails shorthand .to_d 而不是 BigDecimal.new 只是为了简洁,这不是 rails具体问题。)

问题:为什么"0.1".to_d仍然显示10-17数量级的错误?我以为 BigDecimal 的目的是明确避免这样的错误?

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


编辑:

更多的调查表明 BigDecimal 仍然在内部清晰地表示事物。 (很明显,因为否则这将是一个非常广泛使用的系统中的 巨大 错误。)这是一个仍然会显示错误的操作示例:

> puts "%.50f" % ("0.1".to_d * "10".to_d)
1.00000000000000000000000000000000000000000000000000

如果表示有损,则会显示与上述相同的错误,只是偏移了一个数量级。这是怎么回事?

%.50f 说明符采用浮点值,因此十进制值在呈现以显示之前需要转换为浮点值,因此会受到与普通浮点噪声相同的影响浮点值。

sprintf和朋友一样,喜欢String#%方法,根据占位符指定的类型自动进行转换。

要抑制它,您必须直接在 BigDecimal 数字上使用 .to_s method。如果您想要一定数量的位置,它可以采用可选的格式说明符,并且可以将其链接到其他字符串中的 %s 占位符。