C code explanation

1

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?

c
asked on Stack Overflow Jun 19, 2012 by Alex • edited Nov 29, 2013 by BenMorel

1 Answer

5

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):

  • sign bit
  • exponent part (8 bits)
  • significand of the mantissa (23 bits)

f1 is greater than f2 if:

  • f1 is positive and f2 is negative
  • f1 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
  • the opposite of the previous two if f1 and f2 are negative

One 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.

answered on Stack Overflow Jun 19, 2012 by Hristo 'away' Iliev • edited Jun 19, 2012 by Hristo 'away' Iliev

User contributions licensed under CC BY-SA 3.0