Search code examples
c++castingdivisiondouble-precision

How can I trust casting from double to integer?


I've been working on some simple code for creating histograms and found that following code:

double value = 1.2;
double bucketSize = 0.4;
double bucketId = value / bucketSize;

std::cout << "bucketId as double: " << bucketId << std::endl;
std::cout << "bucketId as int: " << int(bucketId) << std::endl;

results in crazy output of:

bucketId as double: 3
bucketId as int: 2

which basically ruins my trust in computers ;) when looking for the right bucketId for the value while creating a histogram.

I know that there are rounding errors etc. but is there any general solution to that problem?

(Just in case) Please don't suggest adding 0.5 to result of the division before casting to int as apparently it doesn't work very well in some cases (e.g. double value = 3; double bucketSize = 2;)

Thanks in advance.


Solution

  • In the comments you say that you want the Integer part of the result. Well, unfortunately the double result of 1.2 / 0.4 just happens to be 2.9999999999999996 (On my machine. You can see the exact value with cout by using std::setprecision) and therefore the integer part of the result is 2. This, as you know, is because not all numbers can be represented with floating point numbers and because floating point operations incur errors.

    Taking the integer part of a floating point number is on the same level as comparing floating point numbers with equality; You are not going to get consistent results. If you must have exact results, the general solution is to not use floating point numbers at all, but fixed point instead.

    As with the equality comparison, you can work around the issue with an appropriate epsilon value. In this case you can add (or subtract if negative) a very small floating point number to the result before taking the integer part. The added number must be larger than the largest possible error the number might have but smaller than the smallest precision number that you must support (so that 9.999 doesn't become 10 if you must support down to 0.001 precision). Figuring out a good number for this can be quite hard.