Search code examples
c++mathfloating-pointnumericfloating-accuracy

Iterate though all possible floating-point values, starting from lowest


I am writing a unit test for a math function and I would like to be able to "walk" all possible floats/doubles.

Due to IEEE shenanigans, floating types cannot be incremented (++) at their extremities. See this question for more details. That answer states :

one can only add multiples of 2^(n-N)

But never mentions what little n is.

A solution to iterate all possible values from +0.0 to +infinity is given in this great blog post. The technique involves using a union with an int to walk the different values of a float. This works due to the following properties explained in the post, though they are only valid for positive numbers.

  1. Adjacent floats have adjacent integer representations
  2. Incrementing the integer representation of a float moves to the next representable float, moving away from zero

His solution for +0.0 to +infinity (0.f to std::numeric_limits<float>::max()) :

union Float_t {
    int32_t RawExponent() const { return (i >> 23) & 0xFF; }
    int32_t i;
    float f;
};

Float_t allFloats;
allFloats.f = 0.0f;
while (allFloats.RawExponent() < 255) {
    allFloats.i += 1;
}

Is there a solution for -infinity to +0.0 (std::numeric_limits<float>::lowest() to 0.f)?

I've tested std::nextafter and std::nexttoward and couldn't get them to work. Maybe this is an MSVC issue?

I would be ok with any sort of hack since this is a unit test. Thanks!


Solution

  • Pascal Cuoq correctly points out std::nextafter is the right solution. I had a problem elsewhere in my code. Sorry for the unnecessary question.

    #include <cassert>
    #include <cmath>
    #include <limits>
    
    float i = std::numeric_limits<float>::lowest();
    float hi = std::numeric_limits<float>::max();
    float new_i = std::nextafterf(i, hi);
    assert(i != new_i);
    
    double d = std::numeric_limits<double>::lowest();
    double hi_d = std::numeric_limits<double>::max();
    double new_d = std::nextafter(d, hi_d);
    assert(d != new_d);
    
    long double ld = std::numeric_limits<long double>::lowest();
    long double hi_ld = std::numeric_limits<long double>::max();
    long double new_ld = std::nextafterl(ld, hi_ld);
    assert(ld != new_ld);
    
    
    for (float d = std::numeric_limits<float>::lowest();
            d < std::numeric_limits<float>::max();
            d = std::nextafterf(
                    d, std::numeric_limits<float>::max())) {
        // Wait a lifetime?
    }