There is a following function that is supposed to make a comparison between 2 floating point values, but faster than regular comparison in some specific cases (e.g. on Cortex-A8)
int isGreater(float* f1, float* f2)
{
int i1, i2, t1, t2;
i1 = *(int*)f1; // reading float as integer
i2 = *(int*)f2; // reading float as integer
t1 = i1 >> 31;
i1 = (i1 ^ t1) + (t1 & 0x80000001);
t2 = i2 >> 31;
i2 = (i2 ^ t2) + (t2 & 0x80000001);
return i1 > i2;
}
Can someone explain how does it exactly work?
This code exploits the structure of the IEEE 754 format for floating point numbers. The structure itself was specifically designed for such operations in order to make comparison operations fast.
Each single-precision IEEE 754 number has three parts (in order from MSB to LSB):
f1is greater thanf2if:f1is positive andf2is negativef1andf2are both positive butf1has greater exponent thanf2f1andf2are both positive and have the same exponents butf1has larger significand thanf2f1andf2are negativeOne could just compare both floating point numbers as integers if they were in two’s complement representation. Unfortunately IEEE 754 doesn’t use two’s complement to represent negative numbers and that’s why this code performs the conversion in order to be able to just compare the numbers as signed integers.
Here is a step by step commentary on what each line of code does:
This one uses the fact that on most 32-bit systems
sizeof(int) == sizeof(float)to read the floating point numbers into regular signed integer variables.This one extracts the sign bit of
f1. Iff1is negative its MSB would be1and hencei1would be negative. Shifting it 31 bits to the right preserves the sign and hence ifi1was negativet1would have all bits set to1(equal to -1). Iff1was positive its sign bit would be0and in the endt1would equal0.If the sign bit was
1this line would perform conversion to two’s complement representation iff1was negative.Here is how it works: if
f1was positive, thent1is0and(i1 ^ t1)would just bei1and(t1 & 0x80000001)would be0and in the endi1would just retain its original value. Iff1was negative thent1would have all bits set to1and the first expression on the RHS would be the bit inversion ofi1and the second expression would equal0x80000001. This wayi1would be converted to its bit inversion and1would be added. But this would lead to a positive number since the MSB would be cleared and that’s why0x80000000is also added to keep the number negative.Perform the same as above for
f2.Just compare the two resulting signed integers. Most CPUs have dedicated instructions to perform signed comparison in hardware.