编辑:我知道浮点运算不准确。算术甚至不是我的问题。加法给出了我预期的结果。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
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.
}
}
我写它是为了检查写成 IEEE 754 单精度浮点数时8099.99975
是否四舍五入。8100
令我惊讶的是,Java 将其转换8099.9995
为浮点字面量 ( 8099.99975f
)。我再次检查了我的计算和 IEEE 标准,但没有发现任何错误。8100
与 as 相距很远,8099.99975
但8099.9995
最后一点8100
是0
应该使其成为正确的表示形式。
所以我检查了 Java 语言规范,看看我是否遗漏了什么。快速搜索后,我发现了两件事:
-
Java 编程语言要求浮点算术表现得好像每个浮点运算符都将其浮点结果四舍五入到结果精度。不精确的结果必须四舍五入到最接近无限精确结果的可表示值;如果两个最接近的可表示值同样接近,则选择其最低有效位为零的值。
-
Java 编程语言在将浮点值转换为整数时使用向零舍入 [...]。
我注意到这里没有提到浮点文字。所以我认为浮点文字可能只是双精度数,当转换为浮点数时,类似于浮点数到整数转换时四舍五入为零。这可以解释为什么8099.99975f
四舍五入为零。
我编写了您可以在上面看到的小程序来检查我的理论,并且确实发现当添加两个应该导致8100
正确浮点数的浮点文字时,会被计算出来。(请注意,8099.9995
and0.00025
可以完全表示为单个浮点数,因此没有可能导致不同结果的舍入)这让我感到困惑,因为浮点文字和计算浮点的行为不同对我来说没有多大意义,所以我挖了语言规范更多,发现了这个:
如果浮点文字以 ASCII 字母 F 或 f [...] 为后缀,则它的类型为 float。float [...] 类型的元素是可以使用 IEEE 754 32 位单精度 [...] 二进制浮点格式表示的值。
这最终表明文字应该根据 IEEE 标准进行四舍五入,在这种情况下是8100
. 那么为什么会这样8099.9995
呢?