How can I trust casting from double to whole?

I worked on some simple histogram code and found that the 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; 

leads to a crazy conclusion:

 bucketId as double: 3 bucketId as int: 2 

which basically destroys my confidence in computers;) when looking for the right bucketId for value when creating a histogram.

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

(Just in case) Please do not offer to add 0.5 to the division result before casting to int , because, apparently, it does not work very well in some cases (for example, double value = 3; double bucketSize = 2; )

Thanks in advance.

+6
source share
8 answers

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

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

As with equality matching, you can work around the problem with the corresponding epsilon value. In this case, you can add (or subtract, if negative) a very small floating point number to the result before accepting the integer part. The number added must be greater than the largest possible error the number may have, but less than the smallest precision number that you must support (so 9.999 will not be 10 if you must maintain accuracy to 0.001). Calculating a good amount for this can be quite complicated.

+3
source

I base this more or less on some of your comments to others. To get the integer part, the solution must use modf . But the integer part 1.2 / 0.4 may well be 2 , not 3 ; 0.4 does not appear in a machine floating point (most of them, at least), so you are sharing something very close to 0.4 .

The real question is what you really want. If you are looking to discretize (does such a word exist) depending on bucketSize , then the correct way to do this is to use scaled integers:

 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 doubles, you must decide how close is to convert 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; } 

You decide which value is appropriate for yourEpsilonHere ; it depends on the application. (Once I used this method, we used 1E-9 . This does not mean that it is suitable for you, however.)

+4
source

Use std::lround . It returns the nearest integer to your double number.

 #include<numeric> double value = 1.2; double bucketSize = 0.4; double bucketId = value / bucketSize; std::cout << "bucketId as int: " << std::lround(bucketId) << std::endl; 

Please note that 3.0/2.0 can still lead to unexpected results, depending on whether the result is 1.4999998 or 1.5000001 naive.

+3
source

Maybe a fixed decimal approximation?

 (int)(value * 100)/(int)(bucketSize *100) 
+3
source

If you want 0.25 before returning the next number (e.g. (double)1.75 - (int)2 ), use int(floor(buckedId+0.25)) .

The point is how much you want to make it round to the previous number.

+2
source
 #include<iostream> #include <limits> int main() { double value = 1.200000000000000; double bucketSize = 0.4000000000000000; double bucketId = value / bucketSize; std::cout.precision(16); std::cout << "bucketId as double: " << std::fixed << bucketId << std::endl; std::cout << "bucketId as int: " << int(bucketId) << std::endl; return 1; } 

try this on your system, would you like something like

bucketId as double: 2.9999999999999996

bucketId as int: 2

and than with

 std::cout.precision(15); 

you would

bucketId as double: 3.000000000000000

bucketId as int: 2

this is because the accuracy limit of double is 15, you can also try to design with a long double and vary the accuracy.

+2
source

I suggest something line by line

 double d = 1.4 / 0.4; int whole = (int)d; int nextWhole = whole + 1; int result = whole; if (fabs(d - nextWhole) < EPSILON) result = nextWhole; 

(this works for positive numbers)

Basically, if your number is so close to the next integer that it doesn't matter, this code will use the next integer.

+2
source

You can use round()

http://www.cplusplus.com/reference/cmath/round/

I believe that this always takes .5 numbers from zero, so if this is bad for your case, this may not be the optimal solution

+1
source

Source: https://habr.com/ru/post/977029/


All Articles