IEEE-754定义了多种浮点类型的标准。多年来,它们都是二进制浮点数;这就是Java的float
和double
所代表的: float
是32位IEEE-754二进制浮点值(标准称之为binary32
)。double
是64位(标准称之为binary64
)。这些二进制浮点数对于计算机进行计算非常高效,但由于它们使用二进制而我们使用十进制,因此存在一些期望不匹配的情况;例如,0.1
无法在double
中精确存储,您会得到奇怪的结果,例如0.1 + 0.2
的结果为0.30000000000000004
。有关详细信息,请参见浮点数计算是否出现问题?。它们不适用于金融计算等场景。
BigDecimal
是一个Java类,它实现了带任意精度的十进制小数。与使用double
相比,它要慢得多,但结果符合我们对十进制的期望(例如,0.1 + 0.2
将会是0.3
)。
IEEE-754的2008版添加了重要的新格式,特别是decimal32
, decimal64
, 和 decimal128
。它们是十进制浮点数,因此它们的工作方式与我们一样。 0.1
可以准确地存储在decimal64
中。0.1 + 0.2
在decimal64
中是0.3
。然而,据我所知,它们并不真正与您的问题相关。
由于 BigDecimal
的发布时间早于 IEEE-754 2008 标准(相差很大),因此它定义了自己的语义。
如果不是这样,那么在 Math 类中使用 IEEE 754 标准有什么意义呢?
JDK9 在 Math
中添加了新的操作,这些操作根据 IEEE-754 2008 规范执行(例如 fma
,它执行 融合乘加运算),因此为了清晰起见,它使用 IEEE-754 2008 规范定义了这些操作。
更多阅读:
BigDecimal
的语义是否与decimalxx
不同?例如,new BigDecimal("0.1", MathContext.DECIMAL64)
是否与decimal64
不同? - SafronBigDecimal
定义了一个带有方法的不可变对象类型,而IEEE-754 decimalXX
类型是原始类型,我怀疑如此。它们可能具有不同的默认舍入行为等等,特别是BigDecimal
的每个实例都可以配置。 - T.J. CrowderDECIMAL64
的文档:一个 MathContext 对象,其精度设置与 IEEE 754-2019 decimal64 格式的精度匹配,即 16 位数字,并采用 HALF_EVEN 舍入模式。(请参见 https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/math/MathContext.html#DECIMAL64) - Safron