Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 217
Moomin Avatar asked Oct 21 '14 13:10

Moomin


1 Answers

I'm basing this more or less on some of your comments to others. To get the integer part, the solution is to use modf. But the integer part of 1.2 / 0.4 could very well be 2, and not 3; 0.4 isn't representable in machine floating point (most of them, at least), so you're dividing by something very close to 0.4.

The real question is what you actually want. If you're looking to discretionize (does such a word exist) depending on bucketSize, then the correct way of doing this is to use scaled integers all around:

int value = 12;
int bucketSize = 4;
int bucketId = value / bucketSize;

and then:

std::cout << "bucketId as double: " << bucketId / 10.0 << std::endl;
std::cout << "bucketId as int: " << bucketId / 10 << std::endl;

Otherwise, if you want to keep the values as double, you will have to decide how close is close for the conversion to int, then use your own function:

int
asInt( double d )
{
    double results;
    double frac = modf( d, &results );
    if ( frac > 1.0 - yourEpsilonHere ) {
        results += 1.0;
    }
    return results;
}

It's up to you to decide what value is appropriate for yourEpsilonHere; it depends on the application. (The one time I used this techique, we used 1E-9. That doesn't mean that it's appropriate for you, however.)

like image 155
James Kanze Avatar answered Sep 25 '22 01:09

James Kanze