【问题标题】:Why does ruby BigDecimal show representation inaccuracy similar to float?为什么 ruby​​ BigDecimal 显示类似于浮点的表示不准确?
【发布时间】:2016-10-14 18:41:08
【问题描述】:

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

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

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

这是nothingnew。但是为什么 ruby​​ 的 BigDecimal 也会出现这种行为呢?

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

(为了简洁起见,我使用 rails 简写 .to_d 而不是 BigDecimal.new,这不是特定于 rails 的问题。)

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

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


编辑:

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

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

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

【问题讨论】:

    标签: ruby floating-point precision bigdecimal


    【解决方案1】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-01-07
      • 1970-01-01
      • 1970-01-01
      • 2012-10-12
      • 2010-11-12
      • 2018-04-25
      • 2021-10-26
      相关资源
      最近更新 更多