views:

225

answers:

5

The following program shows the weird double to int conversion behavior I'm seeing in c++:

#include <stdlib.h>
#include <stdio.h>

int main() {
  double d = 33222.221;
  printf("d = %9.9g\n",d);

  d *= 1000;
  int i = (int)d;

  printf("d = %9.9g | i = %d\n",d,i);

  return 0;
}

When I compile and run the program, I see:

g++ test.cpp
./a.out
d = 33222.221
d =  33222221 | i = 33222220

Why is i not equal to 33222221? The compiler version is GCC 4.3.0

+8  A: 

Floating point representation is almost never precise (only in special cases). Every programmer should read this: What Every Computer Scientist Should Know About Floating-Point Arithmetic

In short - your number is probably 33222220.99999999999999999999999999999999999999999999999999999999999999998 (or something like that), which becomes 33222220 after truncation.

viraptor
Okay thanks, that makes sense. Then what's the preferred method to convert to an int without having to worry about this? The round function? (but that still involves a double->int conversion..)
user924
user924: int i = (int)(d>=0?d+0.5:d-0.5); fixes your problem
MaR
Adding 0.5 ends up with a "round to nearest". If you sort of want "round down", but with a bit of room to correct this kind of imprecision, then you can add a smaller value. For example `(d+0.001)`, or `(d + d*DBL_EPSILON)`. If you're going to be multiplying numbers by 1000 and you want correct results, it might be an idea to get a library for decimal arithmetic.
Steve Jessop
Or just use `i = (int)nearbyint(d)`, from the C99 math library.
Useless
Sure, if the rounding mode matches the way you want to round.
Steve Jessop
A: 

Due to floating point approximation, 33222.221 may actually be 33222.220999999999999. Multiplied by 1000 yields 33222220.999999999999. Casting to integer ignores all decimals (round down) for a final result of 33222220.

Mister
+1  A: 

When you attach a debugger and inspect the values, you will see that the value of d is actually 33222220.999999996, which is correctly truncated to 33222220 when converted to integer.

There is a finite amount of numbers that can be stored in a double variable, and 33222221 is not one of them.

Timbo
Actually, 33222221 is one of them. The problem here is that 33222.221 is not.
Steve Jessop
A: 

If you change the "9.9g" in your printf() calls to 17.17 to recover all possible digits of precision with a 64-bit IEEE 754 FP number, you get 33222220.999999996 for the double value. The int conversion then makes sense.

Warren Young
A: 

I don't want to repeat the explanations of the other comments.

So, here is just an advice to avoid problems like the one described:

  1. Avoid floating point arithemtics in the first place whereever possible (especially when computation is involved).

  2. If floating point arithmetics is really necessary, you must not compare numbers by operator== by all means! Use your own comparison function instead (or use one supplied by some library), which does something like an "is almost equal" comparison using some kind of epsilon compare (either absolute or relative to the number's magniture).

See for example the excellent article

http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

by Bruce Dawson instead!

Stefan

struppi