为什么 new BigDecimal("0.015").compareTo(new BigDecimal(0.015)) return -1?

Why does new BigDecimal("0.015").compareTo(new BigDecimal(0.015)) return -1?

为什么 new BigDecimal("0.015").compareTo(new BigDecimal(0.015)) return -1? 如果我希望这两者相等,是否有其他方法可以比较它们?

由于浮点运算的不精确性,它们并不完全相等

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

显示

0.01499999999999999944488848768742172978818416595458984375

为了扩展@Reimeus 的,BigDecimal 的各种构造函数接受不同类型的输入。 浮点数构造函数,以浮点数为输入,由于floats/doubles存储方式的限制,只能准确存储为2.

因此,例如,2⁻² 或 0.25 可以精确表示。 0.875是(2⁻¹ + 2⁻² + 2⁻³),所以也可以准确表示。只要能用幂次之和来表示这个数,其中上下次幂相差不超过53,那么这个数就可以精确表示。绝大多数数字不符合这种模式!

特别是0.15不是2的幂,也不是2的幂之和,所以表述不准确。

另一方面,字符串构造函数确实通过在内部使用不同的格式来存储数字来准确地存储它。因此,当您比较两者时,它们比较不同。

这里实际发生的是这样的:

  • 0.015 是原始双精度数。也就是说,你一写,就已经不是0.015了,而是0.0149...编译器将其存储为字节码中的二进制表示形式。
  • BigDecimal 的构造是为了 准确地 存储提供给它的任何内容。在这种情况下,0.0149...
  • BigDecimal 也能够将字符串解析为 精确的 表示。在这种情况下 "0.015" 被准确地解析为 0.015。尽管 double 不能表示该数字,但 BigDecimal 可以
  • 最后,当你比较它们时,你会发现它们并不相等。有道理。

每当使用 BigDecimal 时,请注意以前使用的类型。 Stringintlong 将保持精确。 floatdouble 有通常的精度警告。

A double 不能准确表示值 0.015。它可以用其 64 个二进制位表示的最接近的值是 0.01499999999999999944488848768742172978818416595458984375。构造函数 new BigDecimal(double) 旨在保留 double 参数的精确值,它永远不会完全是 0.015。因此,您的比较结果。

但是,如果您显示 double 值,例如:

System.out.println(0.01499999999999999944488848768742172978818416595458984375);

它输出 0.015——这暗示了一个解决方法。 Converting a double to a String 选择最短的十进制表示形式,以区别于其他可能的 double 值。

因此,如果您从 doubleString 表示中创建一个 BigDecimal,它将具有您预期的更多值。这个比较是正确的:

new BigDecimal(Double.toString(0.015)).equals(new BigDecimal("0.015"))

事实上,方法 BigDecimal.valueOf(double) 正是为此目的而存在的,因此您可以将上面的内容缩短为:

BigDecimal.valueOf(0.015).equals(new BigDecimal("0.015"))

仅当您的目的是保留参数的精确二进制值时,才应使用 new BigDecimal(double) 构造函数。否则,调用BigDecimal.valueOf(double),其文档说:

This is generally the preferred way to convert a double (or float) into a BigDecimal.

,如果可以的话使用String并完全避免double的微妙之处。