Ian Rogers writes:
> Ian Rogers wrote:
> > public static int compare(float x, float y)
> > {
> > if (isNaN(x))
> > return isNaN(y) ? 0 : 1;
> > if (isNaN(y))
> > return -1;
> > // recall that 0.0 == -0.0, so we convert to infinities and try again
> > if (x == 0 && y == 0)
> > return (int) (1 / x - 1 / y);
> > if (x == y)
> > return 0;
> > return x > y ? 1 : -1;
> > }
> Below is a variant of the compare code and a comparison with the cost of
> the current compare code:
>
> public static int compare(float x, float y)
> {
> int ix = floatAsIntBits(x);
> int iy = floatAsIntBits(y);
> if (ix == iy) return 0;
> if (isNaN(x)) return 1;
> if (isNaN(y)) return -1;
> int ix_sign = ix>>31;
> int iy_sign = iy>>31;
> if (ix_sign == iy_sign) {
> return x > y ? 1 : -1;
> } else {
> return ix_sign - iy_sign;
> }
> }
>
> Case: x == y neither are NaN or 0.0
> New Cost: 2 float as ints, 1 int compare
> Old Cost: 5 float compares
>
> Case: x < y and different sign
> New Cost: 2 float as ints, 2 int compares, 2 float compares, 2 shifts,
> one subtract
> Old Cost: 6 float compares
>
> Case: x < y and same sign
> New Cost: 2 float as ints, 2 int compares, 3 float compares, 2 shifts
> Old Cost: 6 float compares
>
> Case: x is NaN
> New Cost: 2 float as ints, 1 int compare, 1 float compare
> Old Cost: 2 float compares
>
> Case: y is NaN
> New Cost: 2 float as ints, 1 int compare, 2 float compares
> Old Cost: 2 float compares
>
> Case: x == 0.0 and y == 0.0
> New Cost: either 2 float as ints, 1 int compare or 2 float as ints, 2
> int compares, 2 float compares, 2 shifts, one subtract
> Old Cost: 4 float compares, 2 float divides, 1 float subtract, 1 float
> to int
>
> The same code but for doubles would be:
>
> public static int compare(double x, double y)
> {
> long lx = doubleAsLongBits(x);
> long ly = doubleAsLongBits(y);
> if (lx == ly) return 0;
> if (isNaN(x)) return 1;
> if (isNaN(y)) return -1;
> int lx_sign = (int)(lx>>63);
> int ly_sign = (int)(ly>>63);
> if (lx_sign == ly_sign) {
> return x > y ? 1 : -1;
> } else {
> return lx_sign - ly_sign;
> }
> }
>
> So the case when y is a NaN is slower with the new code but I think all
> of the more common cases are similar or faster. Of course it depends on
> the speed of all the operations. If you cared about a CPU with no
> floating point, the new code is a lot faster. If you've got great
> floating point and a lot of cases of y being a NaN the old code will be
> better. My guess is that in the general case the new code wins out.
> There may be a bug I can't see in this code :-)
We know that any calculation involving NaN returns false, right? So,
simple cases can be done first without the (possibly expensive) move
out of the FPU:
if (x < y)
return -1;
if (x > y)
return 1;
then you can do the doubleToLongBits:
long lx = doubleAsLongBits(x);
long ly = doubleAsLongBits(y);
if (lx == ly)
return 0;
At this point we know they're unequal. Also, either one of them is
NaN, or one of them is -0 and the other +0. Java's doubleToLongBits
alwayse uses the canonical NaN, so we know that they can't both be
NaN.
if (lx < ly)
return -1;
return 1;
Andrew.