等于方法中的浮点/双实例变量的相等比较是否准确? [英] Should Equality Comparison of Float / Double Instance Variables in an Equals Method be Exact?

查看:141
本文介绍了等于方法中的浮点/双实例变量的相等比较是否准确?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm overriding an equality method for an object. Let's say an odometer with a km variable stored as a double (along with some other variables not important for the example).

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;
    }
}

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

Now, the comparison for the double variable as generated by Eclipse (along with the hash code) is an exact bit-wise comparison. However, I've been told to use a "epsilon" difference when comparing float or double values. I've even heard it phrased as "never use equality when comparing floats."

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

JUnit assertEquals double的方法证明了这一点:

The JUnit assertEquals method for doubles bears this out:

assertEquals(double expected, double actual, double epsilon)

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

So, which comparison should I use here?

推荐答案

等于方法的Javadoc声明(重点是我的):

The Javadoc for the equals method states (emphasis mine):

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


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

The equals method implements an equivalence relation on non-null object references:


  • 是自反的:对于任何非空参考值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。

  • It is reflexive: for any non-null reference value x, x.equals(x) should return true.
  • It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
  • It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
  • It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
  • For any non-null reference value x, x.equals(null) should return false.

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

The equality method must be transitive. If you used an epsilon, this won't hold.

考虑double值x = 2.0,y = 2.6,z = 3.1和epsilon = 1.0。

Consider double values x = 2.0, y = 2.6, z = 3.1, and epsilon = 1.0.

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

Note that z - y = 0.5 and y - x = 0.6, both of which are less than the epsilon of 1.0. However, z - x = 1.1 which is more than 1.0.

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

Hence, we would have "x equals y" and "y equals z" but not "x equals z", which breaks transitivity. The same would happen if these were instance variables of some of other object, such as the odometer in the above example.

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

Thus the equality should be exact. Converting to bits as above works, as does using Double.compare(double d1, double d2) or converting them to Double values and then using Double.compareTo(Double anotherDouble). Be aware that these will consider 0.0 and -0.0 as different numbers.

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-

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

This is also important for the sake of keeping the hash function consistent.

即使对于原始双精度值,也不要使用内置的Java等式运算符 == 。如JavaDocs中关于 compareTo 方法所述,相等性因 NaN 而失败。 (此StackOverflow问题还提供了更多信息:为什么Java的Double.compare(double,double)是按原样实现的?

Do not use the built-in Java equality operator == even for primitive double values. As stated in the JavaDocs on the compareTo method, equality fails with NaN. (This StackOverflow question also has some more information: Why is Java's Double.compare(double, double) implemented the way it is?)

最后一点-这不适用于上面的示例,由于使用了原始double值,但是如果使用 Double 对象,请记住在尝试传递它们之前检查 null 插入任何Double比较功能。

One last point - this does not apply to the above example, since primitive double values are used, but if you use Double objects, remember to check for null before trying to pass them into any of the Double comparison functions.

这篇关于等于方法中的浮点/双实例变量的相等比较是否准确?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆