如何防止BigDecimal截断结果? [英] How to prevent BigDecimal from truncating results?

查看:150
本文介绍了如何防止BigDecimal截断结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

关注此问题:

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

I want to calculate 1/1048576 and get the correct result, i.e. 0.00000095367431640625.

使用BigDecimal/会截断结果:

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可以工作.

This really surprised me, because I was under the impression that BigDecimal would just work.

要获得正确的结果,我必须使用显式精度的div:

To get the correct result, I have to use div with an explicit precision:

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

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

但是我不太理解 precision 这个论点.为什么必须指定它,必须使用什么值才能获得未截断的结果?

But I don't really understand that precision argument. Why do I have to specify it and what value do I have to use to get un-truncated results?

这是否符合任意精度浮点十进制算术" ?

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

Furthermore, if I calculate the above value via:

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"

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

I do get the correct result. Why?

推荐答案

BigDecimal可以执行任意精度的浮点十进制算术,但是它不能自动确定给定计算的正确"精度.

BigDecimal can perform arbitrary-precision floating point decimal arithmetic, however it cannot automatically determine the "correct" precision for a given calculation.

例如,考虑

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

可以说,在这种情况下没有正确的精度;正确使用的值取决于计算所需的精度.值得注意的是,从数学意义上讲†,几乎所有整数除法都会导致一个数字具有无限的十进制扩展,因此需要舍入.如果分数被最小化后,分母的唯一质数是2和5,那么分数只有有限的表示形式.

Arguably, there is no correct precision in this case; the right value to use depends on the accuracy required in your calculations. It's worth noting that in a mathematical sense†, almost all whole number divisions result in a number with an infinite decimal expansion, thus requiring rounding. A fraction only has a finite representation if, after reducing it to lowest terms, the denominator's only prime factors are 2 and 5.

因此,您必须指定精度.不幸的是,precision参数有点奇怪,因为它既是有效数字的数量,又是小数点后的数字的数量.这是1/1048576精度不同的

So you have to specify the precision. Unfortunately the precision argument is a little weird, because it seems to be both the number of significant digits and the number of digits after the decimal point. Here's 1/1048576 for varying precision

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位有效数字)

For any value less than 10, BigDecimal truncates the result to 9 digits which is why you get a sudden spike in accuracy at precision 10: at that point is switches to truncating to 18 digits (and then rounds to 10 significant digits).

†取决于您比较无数个无穷集合的大小的舒适程度.

† Depending on how comfortable you are comparing the sizes of countably infinite sets.

这篇关于如何防止BigDecimal截断结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆