views:

265

answers:

2

Is there a way to map floats to ints or unsigned ints so that with the exception of NaN, order is preserved?

So if a and b are floats, and F is the mapping function,

a < b implies F(a) < F(b) and a == b implies F(a) == F(b)

+10  A: 

Hm, just out of the DawsonCompare routine in Game Programming Gems 6, it's a normal bit-cast followed by a sign flip (since negative floats order opposite then negative integers). I'll borrow that idea.

You have:

// utility
template <typename R, typename T>
R& bit_cast(T& pX)
{
    return reinterpret_cast<R&>(pX);
}

// int32_t defined in <boost/cstdint.hpp>. 
boost::int32_t float_to_int_bits(float pX)
{
    boost::int32_t x = bit_cast<boost::int32_t>(pX);

    if (x < 0)
        x = 0x80000000 - x;

    return x;
}

If you can guarantee your int is 32 bits, you can just use that.


Fun fact: The book goes on to use this (note, not with the exact code I present, since I stripped out the float-to-int part) to compare floating point values with tolerance:

bool DawsonCompare(float pX, float pY, int pDiff)
{
    int x = float_to_int_bits(pX);
    int y = float_to_int_bits(pY);

    int diff = x - y;
    return abs(diff) < pDiff;
}

This compares floats as true if their integer representations are within a certain range. (He uses 1000 as a good default.) A branch-less version called the LomontCompare is presented with the same idea, but you have to buy the book for that. :)

GMan
+1 here is a great related link: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
BlueRaja - Danny Pflughoeft
Thanks for the help
zounds
+1  A: 

Just to eliminate the potentially-slow if from the other answers…

int32_t float_to_int( float f ) {
    int32_t i = reinterpret_cast< int32_t& >( f );
    uint32_t sgn = static_cast< uint32_t >( i ) >> 31;

    return i ^ -sgn & numeric_limits<int32_t>::max();
}

Note that unlike the other solution, this incorrectly handles 0 and -0. As floats they compare equal, but after mapping to integers they become 0 and -1, respectively. As a single hiccup in the number line, I don't think it would be easy to handle that case without a branch instruction.

Of course, this assumes two's complement arithmetic, float being IEEE 754 single, same endianness and same address space for floats and ints, etc.

Potatoswatter
GMan's solution handles positive and negative 0 correctly.
Dennis Zickefoose
Ah, now I see, `INT_MIN` => `INT_MIN - INT_MIN`. Thanks.
Potatoswatter