等于方法中的浮点数/双重实例变量的相等比较是否应该精确?

时间:2019-07-17 10:03:25

标签: java floating-point double equals instance-variables

我正在重写对象的相等方法。假设里程表将km变量存储为双精度(以及示例中其他不重要的其他变量)。

public class Odometer { 
    private double km;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        long temp;
        temp = Double.doubleToLongBits(km);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Odometer other = (Odometer) obj;
        if (Double.doubleToLongBits(km) != Double.doubleToLongBits(other.km))
            return false;
        return true;
    }
}

现在,由Eclipse生成的double变量的比较(以及哈希码)是精确的按位比较。但是,有人告诉我在比较float或double值时使用“ epsilon”差异。我什至听说它的短语是“比较浮点数时不要使用相等性。”

boolean equals(double x, double y, double epsilon) { 
    return x - y < epsilon;
}

用于Doubles的JUnit assertEquals方法证明了这一点:

assertEquals(double expected, double actual, double epsilon)

那么,我应该在这里使用哪个比较?

1 个答案:

答案 0 :(得分:6)

equals方法的Javadoc状态(强调我的意思):

https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-

  

equals方法对非null对象引用实现等效关系:

     
      
  • 这是自反的:对于任何非空参考值x,x.equals(x)应该返回true。
  •   
  • 这是对称的:对于x和y的任何非空参考值,当且仅当y.equals(x)返回true时,x.equals(y)才应返回true。
  •   
  • 它是可传递的:对于任何非空参考值x,y和z,如果x.equals(y)返回true,而y.equals(z)返回true,则x.equals(z)应该返回true。
  •   
  • 这是一致的:对于任何非空参考值x和y,只要未修改对象的equals比较中使用的信息,对x.equals(y)的多次调用将始终返回true或始终返回false。
  •   
  • 对于任何非空参考值x,x.equals(null)应该返回false。
  •   

相等方法必须是可传递的。如果您使用了epsilon,则将无法保持。

考虑双精度值x = 2.0,y = 2.6,z = 3.1和epsilon = 1.0。

请注意,z-y = 0.5和y-x = 0.6,两者均小于1.0的ε。但是,z-x = 1.1,比

因此,我们将有“ x等于y”和“ y等于z”,但没有“ x等于z”,这破坏了传递性。如果这些是其他某些对象的实例变量,例如上面示例中的里程表,则会发生同样的情况。

因此,平等应该是精确的。如上使用Double.compare(double d1, double d2)或将它们转换为Double值,然后使用Double.compareTo(Double anotherDouble)一样,将其转换为位是可行的。请注意,它们会将0.0和-0.0视为不同的数字。

https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#compare-double-double- https://docs.oracle.com/javase/8/docs/api/java/lang/Double.html#compareTo-java.lang.Double-

这对于保持哈希函数的一致性也很重要。

即使对于原始双精度值,也不要使用内置的Java等式运算符==。如JavaDocs中关于compareTo方法所述,等于NaN失败。 (此StackOverflow问题还提供了更多信息:Why is Java's Double.compare(double, double) implemented the way it is?

最后一点-由于使用了原始的double值,因此不适用于上面的示例,但是如果您使用Double对象,请记住在尝试将null传递给任何对象之前先进行检查{{1}} Double比较功能。