如何防止BigDecimal截断结果?

时间:2016-11-26 15:52:21

标签: ruby precision bigdecimal

跟进this question

我想计算1/1048576并得到正确的结果,即0.00000095367431640625。

使用BigDecimal' s /截断结果:

require 'bigdecimal'
a = BigDecimal.new(1)
#=> #<BigDecimal:7fd8f18aaf80,'0.1E1',9(27)>
b = BigDecimal.new(2**20)
#=> #<BigDecimal:7fd8f189ed20,'0.1048576E7',9(27)>

n = a / b
#=> #<BigDecimal:7fd8f0898750,'0.9536743164 06E-6',18(36)>

n.to_s('F')
#=> "0.000000953674316406" <- should be ...625

这让我感到很惊讶,因为我认为BigDecimal会起作用。

要获得正确的结果,我必须使用具有明确精度的div

n = a.div(b, 100)
#=> #<BigDecimal:7fd8f29517a8,'0.9536743164 0625E-6',27(126)>

n.to_s('F')
#=> "0.00000095367431640625" <- correct

但我并不完全理解 precision 参数。为什么我必须指定它以及我必须使用什么值来获得未截断的结果?

这是否符合&#34;任意精度浮点十进制算术&#34;

此外,如果我通过以下方式计算上述值:

a = BigDecimal.new(5**20)
#=> #<BigDecimal:7fd8f20ab7e8,'0.9536743164 0625E14',18(27)>
b = BigDecimal.new(10**20)
#=> #<BigDecimal:7fd8f2925ab8,'0.1E21',9(36)>

n = a / b
#=> #<BigDecimal:7fd8f4866148,'0.9536743164 0625E-6',27(54)>

n.to_s('F')
#=> "0.00000095367431640625"

我确实得到了正确的结果。为什么呢?

1 个答案:

答案 0 :(得分:3)

BigDecimal可以执行任意精度浮点十进制算术,但是它无法自动确定给定计算的“正确”精度。

例如,考虑

BigDecimal.new(1)/BigDecimal.new(3)
# <BigDecimal:1cfd748, '0.3333333333 33333333E0', 18(36)>

可以说,在这种情况下没有正确的精度;正确的使用价值取决于计算所需的准确性。值得注意的是,在数学意义上,几乎所有的整数除法都会产生一个具有无限十进制扩展的数字,因此需要舍入。只有在将分数减少到最低项后,分母的唯一素数因子是2和5才有一个有限的表示。

所以你必须指定精度。不幸的是,precision参数有点奇怪,因为它似乎是有效位数和小数点后的位数。这是1/1048576的不同精度

1   0.000001
2   0.00000095
3   0.000000953
9   0.000000953
10  0.0000009536743164
11  0.00000095367431641
12  0.000000953674316406
18  0.000000953674316406
19  0.00000095367431640625

对于任何小于10的值,BigDecimal会将结果截断为9位数,这就是为什么精度为10时精度突然飙升的原因:此时切换为截断为18位(然后舍入为10位有效数字)

†根据您对可数无限集的大小进行比较的舒适程度。