精度丢失 - js & java

一直以来,我们在交易系统中使用BigDecimal类做运算。这成为了一种习惯,导致我忘记了为什么用它。

原因:精度丢失

​ 不管在 java 还是 js ,亦或者 python 中。

​ 0.1 + 0.2 = 0.30000000000000004

​ 所以,为了计算准确,我们使用BigDecimal

js中不存在原生的BigDecimal

替代方案big.jsAPI

Double

img

sign exponent fraction
1位 11位 52位
63 62-52(实际的指数大小-1024 ~ +1023) 51-0

例:3.5

sign: 0

exponent: 10000000000

fraction: 1100000000000000000000000000000000000000000000000000

指数部分10000000000-1023=1

尾数部分11

根据规约形式尾数的整数部分为1

综上,得==1.11B * 10^+1^ = 11.1B = 21 + 20 + 2-1 [^二进制位表示 ] = 2^1^ + 2^0^ + 2^-1^ = 3.5==

3.5刚好可以用二进制表示,但是还有许多小数是无法用二进制表述的

例如:0.1

sign: 0

exponent: 01111111011

fraction: 1001100110011001100110011001100110011001100110011010

无法使用2x + 2y + …这样的形式表示

所以 0.1 + 0.2 == 0.3 false

BigDecimal

解决方案:不适用二进制,而是使用==十进制(BigInteger) + 小数点位置(scale)==来表示。

例如:100.001

unscaledValue:100001

scale:3

即,100.001 = 100001 * 10^-3^

运算操作

加法

x*10^-n^ + y*10^-m^ = x*10^-n^ + (y*10^-m+n^)*10^-n^ = (x + y*10^-m+n^) * 10^-n^ ,其中n > m

乘法

x*10^-n^ * y*10^-m^ = (x*y) * 10^n+m^

BigInteger

可以表示任意精度的整数,当你使用long类型进行运算的时候,可能产生长度溢出,此时就可以考虑使用BigInteger。BigDecimal就是使用它作为backend。

实现方案:==使用数组来存储==

参考资料