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):
f1
is greater than f2
if:
f1
is positive and f2
is negativef1
and f2
are both positive but f1
has greater exponent than f2
f1
and f2
are both positive and have the same exponents but f1
has larger significand than f2
f1
and f2
are 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:
i1 = *(int*)f1; // reading float as integer
i2 = *(int*)f2; // reading float as integer
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.
t1 = i1 >> 31;
This one extracts the sign bit of f1
. If f1
is negative its MSB would be 1
and hence i1
would be negative. Shifting it 31 bits to the right preserves the sign and hence if i1
was negative t1
would have all bits set to 1
(equal to -1). If f1
was positive its sign bit would be 0
and in the end t1
would equal 0
.
i1 = (i1 ^ t1) + (t1 & 0x80000001);
If the sign bit was 1
this line would perform conversion to two's complement representation if f1
was negative.
Here is how it works: if f1
was positive, then t1
is 0
and (i1 ^ t1)
would just be i1
and (t1 & 0x80000001)
would be 0
and in the end i1
would just retain its original value. If f1
was negative then t1
would have all bits set to 1
and the first expression on the RHS would be the bit inversion of i1
and the second expression would equal 0x80000001
. This way i1
would be converted to its bit inversion and 1
would be added. But this would lead to a positive number since the MSB would be cleared and that's why 0x80000000
is also added to keep the number negative.
t2 = i2 >> 31;
i2 = (i2 ^ t2) + (t2 & 0x80000001);
Perform the same as above for f2
.
return i1 > i2;
Just compare the two resulting signed integers. Most CPUs have dedicated instructions to perform signed comparison in hardware.
User contributions licensed under CC BY-SA 3.0