使用 BigDecimal 将两个数字相乘会返回错误的值

2022-09-01 09:41:30

执行以下代码:

new BigDecimal(0.06 * 3).toString()

返回而不是 。0.1799999999999999933386618522490607574582099914550781250.18

执行

new BigDecimal(0.06).multiply(new BigDecimal(3)).toString() 

返回相同的结果。

这怎么可能?


答案 1

您没有使用 将两个数字相乘。您正在使用算术将它们相乘,并将结果传递给构造函数。BigDecimaldoubleBigDecimal

你想要:

new BigDecimal("0.06").multiply(new BigDecimal("3")).toString()

请注意,您确实需要字符串中的值 - 否则您将使用0.06的值,该值并不完全是0.06...您在开始之前丢失了信息。(您并不需要字符串形式的 3,但我这样做是为了保持一致性。double

例如:

System.out.println(new BigDecimal(0.06));

指纹

0.059999999999999997779553950749686919152736663818359375

答案 2

正如Jon Skeet在上面所写的那样,你得到而不是的原因是因为被计算为IEEE 754双精度,然后这个值被转换为.0.1799999999999999933386618522490607574582099914550781250.180.06 * 3doubleBigDecimal

尽管在源代码中看起来足够简单,但数字0.06并不能完全表示为IEEE 754,因此实际表示的是0.06的近似值,等于0.0599999999999999777953953950749686919152736663818359375。十进制表示法中的数字 0.06 不能完全表示,因为该数字等于二进制表示法中的 0b0.000011110101110000101(其中粗体表示重复的数字序列)。计算机必须截断这个无限的二进制数字序列,导致0.0599999...近似值。0.06double0.06

正如我在回答有关 IEEE 754,64 位双精度的问题时所详述的那样,您可以使用 ARIBAS 函数来确定浮点数的尾数和指数:decode_float()

==> set_floatprec(double_float).
-: 64

==> set_printbase(2).
-: 0y10

==> decode_float(0.06).
-: (0y11110101_11000010_10001111_01011100_00101000_11110101_11000010_10001111, 
-0y1000100)

==> set_printbase(10).
-: 10

==> -0y1000100.
-: -68

==> set_floatprec(128).
-: 128

==> 1/2**4 + 1/2**5 + 1/2**6 + 1/2**7 + 1/2**9 + 1/2**11 + 1/2**12 + 1/2**13 + 1/2**18 + 1/2**20.
-: 0.11999_98855_59082_03125_00000_00000_00000_00

(**是 ARIBAS 中的幂)。

我们有 0.06 = Σ i = 0..∞0.11999988555908203125 / 21 + 20 × i

您可以在计算机代数系统(如 Maxima)中评估此序列:

(%i1) sum ( 0.11999988555908203125 / 2 ^ (1 + 20 * i), i, 0, inf ), simpsum;
(%o1)                                0.06

http://maxima-online.org/?inc=r760264757