Ian Rogers wrote:
> The 1st thing is that comparing floats for less-than or greater-than
> won't return true if one of the values is NaN, so it's easy to
> dispatch those most common cases first (and also avoid any float to
> int conversions which are painful as you need to shuffle float
> registers to integer ones and do NaN tests).
Ok, so that is a simple (float1 > float2) and (float2 > float1) check.
> Next doing a comparison
> of raw int bits (avoiding a NaN test if using floatToIntBits) will
> cover the case of equality without returning true for -0.0 and 0.0.
So if the *raw* int bits (call them f1 and f2) are == then return 0.
> Following these 2 cases we either have values that are NaN or
> 0.0/-0.0. Float/Double isNaN has a fast test for NaN that uses a
> single floating point compare, so both parameters need testing to see
> if they are NaN and if they are a result created (the javadoc
> specifies this).
// NaNs are equal to other NaNs and larger than any other float
if (isNaN(float1)) {
if (isNaN(float2)) {
return 0;
}
return 1;
} else if (isNaN(float2)) {
return -1;
}
> Finally we can recycle the raw int bit representation
> and realize that we either have 0x80000000 or 0x00000000 for -0.0 and
> 0.0 (add more 0s for doubles). Therefore an integer comparison will
> return their ordering (the current code in Float.compare uses this
> trick).
(f1 < f2) ? -1 : 1;
> Compared to the current code (in Float) and counting comparisons
> (where the conversion of floatToIntBits requires an additional
> floating-point comparison to detect NaN):
> - If the 2 parameters are regular (ie not NaN) non-equal floating
> point values - upto 2 floating point comparisons are necessary, the
> current code needs 2 integer and 4 floating point comparisons
> - If the 2 parameters are equal but not 0.0 and -0.0 - 2 floating
> point comparisons and 1 integer comparison are necessary, the current
> code needs 3 integer and 3 floating point comparisons
> - If 1 or both of the parameters are NaN then - 4 floating point
> comparisons and 1 integer comparison are necessary, the current code
> needs 2 integer and 2 floating point comparisons (the current code is
> probably faster but this case is unlikely)
> - If the parameters are 0.0 and -0.0 then - 4 floating point
> comparisons and 2 integer comparison are necessary, the current code
> needs 3 integer and 4 floating point comparisons
>
> The main thing is the common case where 2 integer and 2 floating point
> comparisons are saved (and this is frequently executed code if sorting
> an array). For equality then 1 floating point compare and 2 integer
> compares are saved, or in the case of 0.0 and -0.0 (or vice versa) 1
> integer comparison is saved. The code is probably slower when there
> are NaNs, but I believe this case is uncommon.
Cool, take a look at the new version and see if you agree with the changes:
Index: modules/luni/src/main/java/java/lang/Float.java
===================================================================
--- modules/luni/src/main/java/java/lang/Float.java (revision 770260)
+++ modules/luni/src/main/java/java/lang/Float.java (working copy)
@@ -145,25 +145,7 @@
* @since 1.2
*/
public int compareTo(Float object) {
- int f1, f2;
- int NaNbits = Float.floatToIntBits(Float.NaN);
- if ((f1 = Float.floatToIntBits(value)) == NaNbits) {
- if (Float.floatToIntBits(object.value) == NaNbits) {
- return 0;
- }
- return 1;
- }
- if ((f2 = Float.floatToIntBits(object.value)) == NaNbits) {
- return -1;
- }
- if (value == object.value) {
- if (f1 == f2) {
- return 0;
- }
- // check for -0
- return f1 > f2 ? 1 : -1;
- }
- return value > object.value ? 1 : -1;
+ return compare(value, object.value);
}
/**
@@ -402,25 +384,31 @@
* @since 1.4
*/
public static int compare(float float1, float float2) {
- int f1, f2;
- int NaNbits = Float.floatToIntBits(Float.NaN);
- if ((f1 = Float.floatToIntBits(float1)) == NaNbits) {
- if (Float.floatToIntBits(float2) == NaNbits) {
- return 0;
- }
+
+ if (float1 > float2) {
return 1;
}
- if ((f2 = Float.floatToIntBits(float2)) == NaNbits) {
+ if (float2 > float1) {
return -1;
}
- if (float1 == float2) {
- if (f1 == f2) {
+
+ int f1 = floatToRawIntBits(float1);
+ int f2 = floatToRawIntBits(float2);
+ if (f1 == f2) {
+ return 0;
+ }
+
+ // NaNs are equal to other NaNs and larger than any other float
+ if (isNaN(float1)) {
+ if (isNaN(float2)) {
return 0;
}
- // check for -0
- return f1 > f2 ? 1 : -1;
+ return 1;
+ } else if (isNaN(float2)) {
+ return -1;
}
- return float1 > float2 ? 1 : -1;
+
+ return (f1 < f2) ? -1 : 1;
}
/**