Java 为什么是8099.99975f!=8100f
编辑:我知道浮点运算不精确。算术甚至不是我的问题。加法给出了我期望的结果<代码>8099.99975f没有Java 为什么是8099.99975f!=8100f,java,floating-point,ieee-754,single-precision,Java,Floating Point,Ieee 754,Single Precision,编辑:我知道浮点运算不精确。算术甚至不是我的问题。加法给出了我期望的结果8099.99975f没有 所以我有一个小程序: public class Test { public static void main(String[] args) { System.out.println(8099.99975f); // 8099.9995 System.out.println(8099.9995f + 0.00025f); // 8100.0
所以我有一个小程序:
public class Test {
public static void main(String[] args) {
System.out.println(8099.99975f); // 8099.9995
System.out.println(8099.9995f + 0.00025f); // 8100.0
System.out.println(8100f == 8099.99975f); // false
System.out.println(8099.9995f + 0.00025f == 8099.99975f); // false
// I know comparing floats with == can be troublesome
// but here they really should be equal in every bit.
}
}
我写它是为了检查8099.99975
作为IEEE 754单精度浮点写入时是否四舍五入到8100
。令我惊讶的是,Java将它转换为8099.9995
,当它被写成浮点文本(8099.99975f
)时。我再次检查了我的计算和IEEE标准,但没有发现任何错误8100
与8099.99975
的距离与8099.9995
的距离一样远,但8100
的最后一位是0
,这应该是正确的表示
所以我检查了Java语言规范,看看是否遗漏了什么。经过快速搜索,我发现了两件事:
- Java编程语言要求浮点运算的行为就像每个浮点运算符将其浮点结果四舍五入到结果精度一样。不精确的结果必须四舍五入到最接近无限精确结果的可表示值;如果两个最近的可表示值相等接近,则选择其最低有效位为零的值
- Java编程语言在将浮点值转换为整数[…]时使用向零舍入
8099.99975f
四舍五入为零
我编写了上面可以看到的小程序来检查我的理论,并且确实发现,当添加两个浮点文本时,应该计算出8100
正确的浮点值。(请注意,8099.9995
和0.00025
可以精确地表示为单个浮点数,因此不存在可能导致不同结果的舍入)这让我很困惑,因为浮点数文字和计算浮点数的行为不同,这对我来说没有太大意义,所以我在语言规范中进行了更多的研究,发现:
如果浮点文字的后缀为ASCII字母F或F[…],则它的类型为float。浮点[…]类型的元素是那些可以使用IEEE 754 32位单精度[…]二进制浮点格式表示的值
最后指出,应根据IEEE标准对文字进行四舍五入,在本例中,IEEE标准为
8100
。那么为什么是8099.9995
?十进制值8099.99975
有九个有效数字。这超出了在浮点中可以精确表示的范围。如果使用,您将看到最接近8099.9995
的二进制表示形式是45fd1ff
。当您尝试添加0.00025
时,您将遭受“意义缺失”。为了不丢失较大数字的有效位(左侧),必须将较小数字的有效位右移,以匹配较大数字的刻度(指数)。当发生这种情况时,它的值在从寄存器右端移出时变为零
Decimal Exponent Significand
--------- -------------- -------------------------
8099.9995 10001011 (+12) 1.11111010001111111111111
0.00025 01110011 (-12) 1.00000110001001001101111
为了将这些数据进行相加,第二个数据必须右移24位,但单个精度浮点的有效位中只有23位。有效位消失,留下零,因此加法无效
如果您想让它工作,请切换到双精度算术。十进制值
8099.99975
有九个有效数字。这超出了在浮点中可以精确表示的范围。如果使用,您将看到最接近8099.9995
的二进制表示形式是45fd1ff
。当您尝试添加0.00025
时,您将遭受“意义缺失”。为了不丢失较大数字的有效位(左侧),必须将较小数字的有效位右移,以匹配较大数字的刻度(指数)。当发生这种情况时,它的值在从寄存器右端移出时变为零
Decimal Exponent Significand
--------- -------------- -------------------------
8099.9995 10001011 (+12) 1.11111010001111111111111
0.00025 01110011 (-12) 1.00000110001001001101111
为了将这些数据进行相加,第二个数据必须右移24位,但单个精度浮点的有效位中只有23位。有效位消失,留下零,因此加法无效
如果你想这样做,就切换到双精度算术。要认识到的关键点是,浮点数的值可以用两种不同的方法计算出来,这两种方法通常并不相等
- 浮点数中的位给出了的值的精确二进制表示形式
- 这里有一个浮点数的“十进制显示值”,这是一个小数位数最少的数字,它比任何其他数字都更接近该浮点数