For the purpose of ordering floating-point numbers in a data structure which doesn't allow duplicates (like a binary tree, heap, etc.), I want to round float
to a certain level of precision, as follows:
float round_to_epsilon(x);
bool less_than(float x, float y) {
return round_to_epsilon(x) < round_to_epsilon(y);
}
In other words, I want to quantize float
s to multiples of std::numeric_limits<float>::epsilon
, which is 2-23.
1st Idea: Multiply and Floor
At first glance, it may seem like you could just do:
float round_to_epsilon(float x) {
// note: floor or ceil must be used to avoid bias around zero
return std::floor(x / std::numeric_limits<float>::epsilon());
}
However, this will turn every number greater than 2105 into positive infinity, which is not overflow-correct.
2nd Idea: Divide to Discard Precision
An alternative is to multiply by a really small number to intentionally cause underflow, and drop some digits. However, this makes denormalized numbers quite likely, so there may be serious performance issues with such an approach.
Question
How can I round to epsilon; or in other words, how can I drop digits below a magnitude of 2-23, without causing unnecessary overflow or underflow?
My intuition is that perhaps I could divide by a power of two and then multiply again to obtain avoid denormalized numbers, but I'm unsure how to do that, or whether it is a good approach.