对于一个Java程序,我编写了一个计算两个浮点数之间距离的方法。 该方法对于输入顺序对称地工作是非常重要的。 目前我正在使用的方法:
double distanceBetween(double a, double b) {
return Math.abs(a - b); // I assume that a-b = -(b-a)
}
但我不确定这是否总是对称的,或者我是否应该将代码更改为:
double distanceBetween(double a, double b) {
if (a > b) return a - b;
else return b - a;
}
所以我的问题是,是
Math.abs(a - b) == Math.abs(b - a)
保证 a 和 b 的所有值始终为真? 或者在某些情况下(由于使用浮点数学)结果是否可能不完全相同?
如果我们假设
a
和b
都是有效数字,即不是NaN,那么根据IEEE 754
(这是java遵循的标准)减法运算需要在运算之前对指数部分进行均衡,这意味着其中一个数字被移动以对齐尾数部分
标准建议移动较小的数字,即保留较大数字的最高有效位,因此对于这两种情况(
a-b
和b-a
),较小的数字总是被移动,而较大的数字不受影响,因此在这两种变体中,相同的变量实际上将被移动移位,因此实际的减法运算是在两个变体的完全相同的尾数位模式上执行的,只有最终符号不同,因此我们可以说 a-b
始终等于 b-a
(但对于超过 2 个变量,情况并非如此)在公式中,因为每次二元运算后可能进行舍入,即 a-b-c != a-(b+c)
)
一些来源:
计算两个浮点数之间的距离是否对称?
在 Java 原生浮点运算中是,在其他语言中使用非对称舍入方法时则不是,在 Java 的
BigDecimal
中使用非对称舍入方法时也不是。
IEEE 754-2019 5.1 说:
…除非另有规定,本标准规定的返回数值结果的每个计算操作都应像首先产生一个精确到无限精度和无界范围的中间结果一样执行,然后在必要时对该中间结果进行舍入,以适应目的地的格式(参见 4 和 7)...
减法和绝对值是标准指定的返回数字结果的运算。 IEEE 754 浮点格式在符号方面是对称的;对于任何可表示的数字 x,−x 也是可表示的。因此,使用任何对称舍入方法,如果 a−b 舍入为某个数字 x,则 b−a 舍入为 −x。
IEEE 754 指定了非对称舍入模式,例如向无穷大舍入(向上舍入)。使用非对称舍入模式,a−b 并不总是对称舍入为 b−a。例如,对于假设的两位十进制浮点数,
14 - 1.7
将产生结果 13(14 − 1.7 = 12.3,向上舍入为 13),但 1.7 − 14 将产生 −12 (1.7 − 14 = −12.3,向上舍入为 −12)。
但是,Java 对其本机浮点运算不使用非对称舍入模式(Java 虚拟机规范,Java SE 18 版,2022 年 2 月 23 日,第 2.8 节,第 20 页)。
取绝对值永远不会四舍五入,因为它的数学结果总是可表示的,因此它不是分析中的一个因素。
Java 的
BigDecimal
支持不对称舍入方法,因此 a−b 和 b−a 并不总是在 BigDecimal
中产生对称结果。