比较浮点值
使用关系运算符比较浮点值(float
或 double
)时应该小心:==
,!=
,<
等。这些运算符根据浮点值的二进制表示给出结果。例如:
public class CompareTest {
public static void main(String[] args) {
double oneThird = 1.0 / 3.0;
double one = oneThird * 3;
System.out.println(one == 1.0); // prints "false"
}
}
计算 oneThird
引入了一个很小的舍入误差,当我们将 oneThird
乘以 3
时,得到的结果与 1.0
略有不同。
当我们尝试在计算中混合 double
和 float
时,这种不精确表示的问题更加明显。例如:
public class CompareTest2 {
public static void main(String[] args) {
float floatVal = 0.1f;
double doubleVal = 0.1;
double doubleValCopy = floatVal;
System.out.println(floatVal); // 0.1
System.out.println(doubleVal); // 0.1
System.out.println(doubleValCopy); // 0.10000000149011612
System.out.println(floatVal == doubleVal); // false
System.out.println(doubleVal == doubleValCopy); // false
}
}
Java 中用于 float
和 double
类型的浮点表示具有有限的精度位数。对于 float
类型,精度是 23 个二进制数字或大约 8 个十进制数字。对于 double
类型,它是 52 位或大约 15 个十进制数字。最重要的是,一些算术运算会引入舍入误差。因此,当程序比较浮点值时,标准做法是为比较定义可接受的增量。如果两个数字之间的差异小于 delta,则认为它们相等。例如
if (Math.abs(v1 - v2) < delta)
Delta 比较示例:
public class DeltaCompareExample {
private static boolean deltaCompare(double v1, double v2, double delta) {
// return true iff the difference between v1 and v2 is less than delta
return Math.abs(v1 - v2) < delta;
}
public static void main(String[] args) {
double[] doubles = {1.0, 1.0001, 1.0000001, 1.000000001, 1.0000000000001};
double[] deltas = {0.01, 0.00001, 0.0000001, 0.0000000001, 0};
// loop through all of deltas initialized above
for (int j = 0; j < deltas.length; j++) {
double delta = deltas[j];
System.out.println("delta: " + delta);
// loop through all of the doubles initialized above
for (int i = 0; i < doubles.length - 1; i++) {
double d1 = doubles[i];
double d2 = doubles[i + 1];
boolean result = deltaCompare(d1, d2, delta);
System.out.println("" + d1 + " == " + d2 + " ? " + result);
}
System.out.println();
}
}
}
结果:
delta: 0.01
1.0 == 1.0001 ? true
1.0001 == 1.0000001 ? true
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-5
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-7
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? true
1.000000001 == 1.0000000000001 ? true
delta: 1.0E-10
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false
delta: 0.0
1.0 == 1.0001 ? false
1.0001 == 1.0000001 ? false
1.0000001 == 1.000000001 ? false
1.000000001 == 1.0000000000001 ? false
另外,为了比较 double
和 float
原始类型,可以使用相应拳击类型的静态 compare
方法。例如:
double a = 1.0;
double b = 1.0001;
System.out.println(Double.compare(a, b));//-1
System.out.println(Double.compare(b, a));//1
最后,确定哪种增量最适合进行比较可能会非常棘手。一种常用的方法是选择 delta 值,这是我们的直觉所说的正确。但是,如果你知道输入值的比例和(真实)准确度以及执行的计算,则可能会在结果的准确性上提出数学上的声音界限,从而得出增量。 (有一个称为数值分析的正式数学分支,曾经被教给计算科学家,涵盖了这种分析。)