views:

1428

answers:

11

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.

+37  A: 

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.

Nils Pipenbrinck
You specifically do NOT want to handle mantissa overflow, since the overflow will roll over onto the exponent which is what you want.
Cool - I never thought about that. Incrementing the float as an integer will exactly do what needed.
Nils Pipenbrinck
It is cool :) Now could the idiot who downvoted my answer saying so please undo it?
How would that work if the exponent increment overflowed into the sign bit?
Greg Rogers
Yep, you would need special cases for inf and nan.
I think negative values have to be treated different as well. If you increment those the result will be more negative than the original value. And btw - I just disassembled nextafter in the VS.NET 2008 implementation. They do quite a bit more than I would have expected.
Nils Pipenbrinck
Nils: Ah, v. true about -ve numbers.
A: 

This post on StackOverflow has some information that would be relevant.

Mark Ransom
+4  A: 

I don't know about in C and C++, but if you can convert the raw bits to an integer (the equivalent of Convert.DoubleToInt64Bits in .NET) then increment the integer, then convert back again, that's your answer - assuming the processor uses IEEE 754. The IEEE format is very cunningly designed so that if you sort a collection of floating point numbers by treating them as integers, you get the right order.

Sorry it's only half an answer, but hopefully it'll point in the right direction. My C/C++ is rusty, but you may be able to use:

long *x = &f;
(*x)++;

or something similar. Worth a try - and verifying with someone more on-the-ball :)

Jon Skeet
OIC: floating point numbers were designed by the IEEE committee; the draft evolved; there are many copies of it; and IEEE had a cunning plan.
ΤΖΩΤΖΙΟΥ
undefined behaviour, violation of strict aliasing rules
sellibitze
+10  A: 
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;
}
I wonder if the downvoter could comment as to why this wasn't helpful... Myself, having learned of the nextafter() function, I'd prefer those but if this one would work then I figure it's noteworthy in its own right.
Owen
Mike, what's the include file / compiler that will make this run?
David Nehme
This is totally implementation dependent and non-portable. Works ok if you have EEMMM, but if you have MMMEE won't give you the result you want.
Benoit
@David: Try replacing u64 with 'long long'. @Benoit: yep, it assumes ieee754, I think that's a fairly safe bet nowadays.
undefined behaviour, violation of strict aliasing rules
sellibitze
@sellibitze: possibly undefined but in practice C compilers that do not handle this kind of type-casting will not be able to build real-world code. So it isn't a problem unless you get over happy with optimization settings.
Zan Lynx
+3  A: 

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.

moonshadow
+1  A: 

I found this code a while back, maybe it will help you determine the smallest you can push it up by then just increment it by that value. Unfortunately i can't remember the reference for this code:

#include <stdio.h>

int main()
{
    /* two numbers to work with */
    double number1, number2;    // result of calculation
    double result;
    int counter;     // loop counter and accuracy check

    number1 = 1.0;
    number2 = 1.0;
    counter = 0;

    while (number1 + number2 != number1) {
     ++counter;
     number2 = number2 / 10;
    }
    printf("%2d digits accuracy in calculations\n", counter);

    number2 = 1.0;
    counter = 0;

    while (1) {
     result = number1 + number2;
     if (result == number1)
      break;
     ++counter;
     number2 = number2 / 10.0;
    }

    printf("%2d digits accuracy in storage\n", counter );

    return (0);
}
PixelSmack
+2  A: 

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;
}
Jim Buck
good one as well!
Nils Pipenbrinck
+1  A: 

For what it's worth, the value for which standard ++ incrementing ceases to function is 9,007,199,254,740,992.

TheSoftwareJedi
A: 

There should be constant or function named epsilon. You can increment by the value of epsilon. http://msdn.microsoft.com/en-us/library/6x7575x3(VS.80).aspx

Roger Nelson
I don't think this is the answer. If 1+epsilon != 1, this will not imply that 2+epsilon != 2...
botismarius
+7  A: 

You've had some specific advise for this case already, but on top of that --- if you haven't already I'll urge you to read What Every Computer Scientist Should Know About Floating-Point Arithmetic. Same goes for anyone else reading this thread who is uncertain of how they should manage floats, really. It's well written, and clears up a number of common misconceptions about floating point representations.

simon
+1  A: 

This may not be exactly what you want, but you still might find numeric_limits in of use. Particularly the members min(), and epsilon().

I don't believe that something like mydouble + numeric_limits::epsilon() will do what you want, unless mydouble is already close to epsilon. If it is, then you're in luck.

luke