Search code examples
c++opencvcastingieee-754feature-extraction

Toggle float macro together with int* - What does it do?


I am trying to understand the AKAZE feature implementation in OpenCV and was baffled when I came accross the following macro:

/* IEEE754 constants and macros */
#define  CV_TOGGLE_FLT(x) ((x)^((int)(x) < 0 ? 0x7fffffff : 0))

It is used in the MLDB binary comparisons in the following way (I removed all irrelevant parts of the code):

void MLDB_Binary_Comparisons(float* values)
{
    int* ivalues = (int*)values;
    for (int i = 0; i < count * chan; i++)
        ivalues[i] = CV_TOGGLE_FLT(ivalues[i]);

    /* ... The ivalues are then compared to each other using the > operator */
}

Does anyone understand what's going on here? Is it really safe to cast a float* to an int*, and what on earth does the macro do?

Thanks in advance!


Solution

  • In essence, it does some binary manipulation of the floating-point data, in a non-portable fashion: there are clearly assumptions about the relative sizes of int and float and also about their representation. I think that if int is a 32-bit 2's-complement integer, and float is an IEEE 32-bit floating point value, then it lets you compare the floating point values using only integer operations.

    In more detail...

    int* ivalues = (int*)values;
    

    This just gives us a pointer that is used to walk through the floating-point values. They are interpreted as ints, not converted to int - in other words there is no reason to think that a float with a value of 1.0 will map to an int value of 1. So the main thing you can do is bitwise operations, or specific manipulations which rely on understanding the binary format used for float.

    Looking at the macro...

    #define  CV_TOGGLE_FLT(x) ((x)^((int)(x) < 0 ? 0x7fffffff : 0))
    

    The value x (remember, just some of the bits which otherwise represent a floating point value, but here we are just looking at it as a bit pattern) is XOR-ed with either 0 (which has no effect) or 0x7fffffff (which inverts the low-order 31 bits). The decision of what to do is based on whether x is less than zero or not.

    I think this macro is based on the assumption that an int is a 32-bit 2's-complement representation. That means that x < 0 is true if the top bit is set. The XOR with 0x7fffffff will invert the other bits. This will mean that -1, the most positive negative number, is changed from 0xffffffff to 0x8000000, which is the most negative negative number (-2147483648) and likewise 0x8000000 becomes 0xffffffff. When you use > after this to compare two negative numbers, it means that what was the more negative number is now the more positive number, and the "greater than" branch will be taken. Comparing two positive numbers is unchanged, and comparing a positive number to a negative number the positive number is, as you'd expect, still always greater.

    This lets you do comparisons for floats using only integer operations. This is because the way floats, in IEEE format, store negative numbers is quite different from 2's-complement: the sign bit is really just a flag that the value is positive or negative, and the remaining bits store the magnitude of the number. Once you have applied the CV_TOGGLE_FLT(x) you don't have values you can use directly, but they are ordered in the same fashion as the original float values.