How to alter a float by its smallest increment (or close to it)?
I have a double
value f
and would like a way to nudge it very slightly larger (or smaller) to get a new value that will be as close as possible to the original but still strictly greater than (or less than) the original.
It doesn't have to be close down to the last bit—it's more important that whatever change I make is guaranteed to produce a different value and not round back to the original.
Check your math.h file. If you're lucky you have the nextafter
and nextafterf
functions defined. They do exactly what you want in a portable and platform independent way and are part of the C99 standard.
Another way to do it (could be a fallback solution) is to decompose your float into the mantissa and exponent part. Incrementing is easy: Just add one to the mantissa. If you get an overflow you have to handle this by incrementing your exponent. Decrementing works the same way.
EDIT: As pointed out in the comments it is sufficient to just increment the float in it's binary representation. The mantissa-overflow will increment the exponent, and that's exactly what we want.
That's in a nutshell the same thing that nextafter does.
This won't be completely portable though. You would have to deal with endianess and the fact that not all machines do have IEEE floats (ok - the last reason is more academic).
Also handling NAN's and infinites can be a bit tricky. You cannot simply increment them as they are by definition not numbers.
u64 &x = *(u64*)(&f);
x++;
Yes, seriously.
Edit: As someone pointed out, this does not deal with -ve numbers, Inf, Nan or overflow properly. A safer version of the above is
u64 &x = *(u64*)(&f);
if( ((x>>52) & 2047) != 2047 ) //if exponent is all 1's then f is a nan or inf.
{
x += f>0 ? 1 : -1;
}
In absolute terms, the smallest amount you can add to a floating point value to make a new distinct value will depend on the current magnitude of the value; it will be the type's machine epsilon multiplied by the current exponent.
Check out the IEEE spec for floating point represenation. The simplest way would be to reinterpret the value as an integer type, add 1, then check (if you care) that you haven't flipped the sign or generated a NaN by examining the sign and exponent bits.
Alternatively, you could use frexp to obtain the current mantissa and exponent, and hence calculate a value to add.
I needed to do the exact same thing and came up with this code:
double DoubleIncrement(double value)
{
int exponent;
double mantissa = frexp(value, &exponent);
if(mantissa == 0)
return DBL_MIN;
mantissa += DBL_EPSILON/2.0f;
value = ldexp(mantissa, exponent);
return value;
}