我正在阅读Joshua Bloch的有效java和第8项:当重写等于时遵守一般合同,这句话是写的
对于float字段,使用Float.compare方法;对于双字段,请使用 Double.compare。浮法和双场的特殊处理 由Float.NaN,-0.0f和类似的双重存在所必需的 常数;
有人可以用例子向我解释为什么我们不能将==
用于浮动或双重比较
答案 0 :(得分:18)
来自apidoc,Float.compare
:
比较两个指定的浮点值。返回的整数值的符号与调用返回的整数的符号相同:
new Float(f1).compareTo(new Float(f2))
以数字方式比较两个Float对象。当应用于原始浮点值时,此方法执行的比较有两种方式与Java语言数值比较运算符(<,< =,==,> =>)执行的比较不同:
- 此方法认为Float.NaN等于自身且大于所有其他浮点值(包括Float.POSITIVE_INFINITY)。
通过该方法认为- 0.0f大于-0.0f。
这可确保此方法强加的Float对象的自然顺序与equals一致。
请考虑以下代码:
System.out.println(-0.0f == 0.0f); //true
System.out.println(Float.compare(-0.0f, 0.0f) == 0 ? true : false); //false
System.out.println(Float.NaN == Float.NaN);//false
System.out.println(Float.compare(Float.NaN, Float.NaN) == 0 ? true : false); //true
System.out.println(-0.0d == 0.0d); //true
System.out.println(Double.compare(-0.0d, 0.0d) == 0 ? true : false);//false
System.out.println(Double.NaN == Double.NaN);//false
System.out.println(Double.compare(Double.NaN, Double.NaN) == 0 ? true : false);//true
输出不正确,因为不是数字的东西根本不是数字,从数字比较的角度来看应该被视为相等。同样很清楚0=-0
。
让我们看看Float.compare
做了什么:
public static int compare(float f1, float f2) {
if (f1 < f2)
return -1; // Neither val is NaN, thisVal is smaller
if (f1 > f2)
return 1; // Neither val is NaN, thisVal is larger
int thisBits = Float.floatToIntBits(f1);
int anotherBits = Float.floatToIntBits(f2);
return (thisBits == anotherBits ? 0 : // Values are equal
(thisBits < anotherBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN)
1)); // (0.0, -0.0) or (NaN, !NaN)
}
根据 IEEE 754浮点“单一格式”位布局返回指定浮点值的表示。 位31(由掩码0x80000000选择的位)表示浮点数的符号。位30-23(由掩码0x7f800000选择的位)表示指数。位22-0(由掩码0x007fffff选择的位)表示浮点数的有效位数(有时称为尾数)。
如果参数为正无穷大,则结果为0x7f800000。
如果参数为负无穷大,则结果为0xff800000。
如果参数为NaN,则结果为0x7fc00000。
在所有情况下,结果都是一个整数,当赋予intBitsToFloat(int)方法时,它将生成一个与floatToIntBits的参数相同的浮点值(除了所有NaN值都折叠为a单一的“规范”NaN值)。
来自JLS 15.20.1. Numerical Comparison Operators <, <=, >, and >=
根据IEEE 754标准规范确定的浮点比较结果如下:
如果任一操作数为NaN,则结果为false。
NaN以外的所有值都是有序的,负无穷大小于所有有限值,正无穷大大于所有有限值。
正零和负零被认为是相等的。例如,-0.0 <0.0为假,但-0.0 <= 0.0为真。
但请注意,方法Math.min和Math.max将负零视为严格小于正零。
对于操作数为正零和负零的严格比较,结果将是错误的。
来自JLS 15.21.1. Numerical Equality Operators == and !=:
根据IEEE 754标准规范确定的浮点比较结果如下:
根据IEEE 754标准的规则执行浮点相等测试:
如果任一操作数为NaN,则==的结果为false,但!=的结果为true。实际上,当且仅当x的值是NaN时,测试x!= x才为真。方法Float.isNaN和Double.isNaN也可用于测试值是否为NaN。
正零和负零被认为是相等的。例如,-0.0 == 0.0为真。
否则,等于运算符会将两个不同的浮点值视为不相等。特别是,有一个值代表正无穷大,一个值代表负无穷大;每个比较仅与自身相等,每个比较不等于所有其他值。
对于两个操作数均为NaN的相等比较,结果将是错误的。
由于许多重要算法使用total ordering (=
, <
, >
,<=
, >=
)(请参阅all the classes that implement the Comparable interface),因此最好使用compare方法,因为它会产生更一致的行为。
total ordering in the context of the IEEE-754 standard的结果是正负零之间的差异。
例如,如果使用等于运算符而不是compare方法,并且有一些值集合,并且您的代码逻辑根据元素的顺序做出一些决定,并且您以某种方式开始获得剩余的NaN值将被视为不同的值而不是相同的值。
这可能会在程序行为中产生与NaN值的数量/速率成比例的错误。如果你有很多正负零,那只是一对会影响你的逻辑错误。
答案 1 :(得分:5)
float
(和double
)有一些特殊的比特序列保留用于非“数字”的特殊含义:
0xff800000
0x7f800000
0x7fc00000
当使用0
与自身进行比较时,每个回复Float.compare()
(意味着它们“相同”),但使用==
的以下比较与此Float.NaN
不同}:
Float.NEGATIVE_INFINITY == Float.NEGATIVE_INFINITY // true
Float.POSITIVE_INFINITY == Float.POSITIVE_INFINITY // true
Float.NaN == Float.NaN // false
因此,在比较float
值时,要使所有值保持一致,包括特殊Float.NaN
值,Float.compare()
是最佳选择。
同样适用于double
。
答案 2 :(得分:2)
比较浮点对象有两个原因:
==
运算符提供数学比较。它为NaN == NaN
返回false,对-0.f == +0.f
compare
和compareTo
例程提供对象比较。当比较NaN与它自身时,它们表明它是相同的(通过返回零)。将-0.f
与+0.f
进行比较时,它们表明它们不同(通过返回非零值)。