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