Rounding from float to integer issues, (int)12.7*10 equals 126 instead of 127?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <string>

using namespace std;

#include <iostream>
#include <string>

using namespace std;

int main()
{
    while (1) {

        float fnum = 0;
        cin >> fnum;
        int num = fnum * 10.0;
        cout << "fnum:" << fnum << " num:" << num << endl;
    }
}



12.5
fnum:12.5 num:125

12.6
fnum:12.6 num:126

12.7
fnum:12.7 num:126


Why would 12.7*10 be 126 as an integer instead of 127? Are there anyways around of such terrible rounding?
Last edited on
Because exact math does not work, in general, on floating-point numbers. 12.6 and 12.7 are not perfectly representable in binary floating-point notation.

The result of multiplying 12.7 * 10 is a number that is very close to 127, but not exactly 127. Just a little less, actually.

Please run this to see for yourself:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

using namespace std;

int main()
{
    float a = 12.5;
    float b = 12.6;
    float c = 12.7;
    
    cout.precision(16); // Show enough decimal places
    
    std::cout << a << std::endl;
    std::cout << b << std::endl;
    std::cout << c << std::endl;
    
    std::cout << "\n";
       
    std::cout << a * 10.0 << std::endl;
    std::cout << b * 10.0 << std::endl;
    std::cout << c * 10.0 << std::endl;

}

12.5
12.60000038146973
12.69999980926514

125
126.0000038146973
126.9999980926514


Furthermore, there is no "rounding" done, the value is simply truncated -- 126.999 becomes 126.

Try using std::round, defined in <cmath> header
http://www.cplusplus.com/reference/cmath/round/
Last edited on
When converting from a floating point type to an integer it always rounds towards zero (i.e. the decimal part is simply lost). If you want to round to the closest integer value you could use std::round.

 
int num = std::round(fnum * 10.0);

http://www.cplusplus.com/reference/cmath/round/
You need to realize that 10.0 is a double number, I am not an expert at floating point shenanigans but all I know is that you should not be mixing float between double ever.

You should be using 10.f to designate literals as a float, or if you are dealing with variables you have to cast a conversion from float to double.

Or you could replace float with double.
You need to realize that 10.0 is a double number, I am not an expert at floating point shenanigans but all I know is that you should not be mixing float between double ever.

Not really an issue. This corrupts scientific level data to the lowest precision, but it is quite 'safe' otherwise. Internally, whether its an integer or a float or a double or a 10B or whatever else it is all converted to the native FPU format (a small overhead if not in the native format, which used to be 10B). Then it cooks up the answer and puts out the result back into the format requested (int, char, double, float, whatever) again getting a tiny overhead in the conversion.

Its a good idea not to mix them, but as long as you understand that the extra precision of the doubles are corrupted and it is only as accurate as a float afterwards, it is safe to do.

if you are doing high precision work, use the 10bs. Most compilers have some sort of nonstandard way to get at them. Just be aware that these have their own problems -- the idea is that the extra precsion of the 10b ensures that the doubles are correct, so if you use 10b directly, you have roundoff problems and accumulation of error in the FPU results and have to account for that. 10B have the same problems as floats and doubles, it just has more precision and can represent a larger subset of integers precisely.
Well, it seems to make a difference in this case. When I ran the original program I got the same output as Fadey. When I changed the program to only use floats, or only doubles, it started to output the "correct" value 127.
Last edited on
Rounding from float to integer issues

As Ganado explained, you aren't rounding at all. It's truncating the number. To round to an integer, just add 0.5
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <string>

using namespace std;

int main()
{
    float fnum;
    while (cin >> fnum) {
        int num = fnum * 10.0 + 0.5;
        cout << "fnum:" << fnum << " num:" << num << endl;
    }
}

When I changed the program to only use floats, or only doubles, it started to output the "correct" value 127.
Using double changes which values give wrong answer but doesn't remove the bug. You have to round off the number.
To round to an integer, just add 0.5

That only works for positive values.
+0.5 rounding is evil and the cases where you absolutely need it is very rare (probably more common in school homework then in real world examples).

When in doubt, rounding downward to the nearest decimal is usually the desired result.

I remember trying to use the +0.5 when trying to write an fps timer since I had trailing .999, but then it would then 61 fps would start showing occasionally when the actual inaccuracy doesn't go that far when you look at the individual frames. Then I fixed it by changing an accidental double literal.

Instead of doing +0.5, you might be able to do a quick hack by just adding an epsilon to the value before making it an integer, but that would be a bad practice since I bet a problem with trailing (very close but wrong) numbers is probably due to mixing float with doubles.

and @jonnin is almost correct, like I believe addition with floats and doubles are quite safe (at least with small number and not being deep in the decimal places), this also works flawlessly with literals that are calculated at compile time. But when you are dealing with multiplication and division, in the cases which result in repeating numbers and since the value is a float, it looks kinda like 1.999990000, then it gets converted to a double since double is on the right (due to order of operations, this is also a common noob mistake especially when they divide by a integer, you can easily divide a integer with a float, as long as the float is on the right side), then the 0's are added in, then it gets converted back to float, where the 0 at the end of the 9's prevents the whole number becoming 2 (at least that's how it makes most sense to me, and why my post fixes the problem).
Have a go at writing 12.7 in binary, @Fadey - then come back and tell us that rounding is terrible.


poteto wrote:
+0.5 rounding is evil

Oh, it's not that bad! It does at least have a precise (and simple) representation in binary, so you won't be losing accuracy by effecting it.
Sadly, I use it a lot. Which makes me evil!
Last edited on
...did someone remove Fadey's post? Weird.

Anyway, I wouldn't necessarily call +0.5 evil, but I don't know the exact situation of your fps counter. Perhaps give us the particular numbers that are causing a value <= 60.0 to be rounded to 61? Of course, floating-point math is annoying as hell once you get into the details of it. There is one particular case (for doubles) where +0.5 does what most would consider is incorrect behavior:
1
2
3
4
5
6
7
8
9
// Example program
#include <iostream>
#include <cmath>

int main()
{
    std::cout << static_cast<int>(0.49999999999999994 + 0.5) << "\n";
    std::cout << std::round(0.49999999999999994) << "\n";
}

0.49999999999999994 + 0.5 turns out to be exactly 1.0 in IEEE 754 double-precision binary floating-point format. std::round accounts for this. Of course, if it really matters whether or not 0.49999... gets rounded to 0.0 or 1.0, I would consider a re-design of what you're doing. In most applications, this would just be considered noise, and it wouldn't matter.

Slightly related: If we're trying to round, for example, 0.55 to either 0.5 or 0.6 (rounding something to a certain number of significant digits, but not necessarily to an integer, and ignoring floating-point problems), +0.5 should not be done, rather you should look at the digit before the digit you're trying to round off, and round down (towards zero) if the digit is even, or round up (away from zero) if the digit is odd (or vice-versa, it's arbitrary).

For example,
0.65 would get rounded towards zero to 0.6 since 6 is even.
0.75 would get rounded away from zero to 0.8 since 7 is odd.
-0.75 would get rounded away from zero to -0.8 since 7 is odd.

The purpose of this is to keep the statistical bias as close to possible to 0, assuming the random numbers you get are uniform enough. This would be applicable in an engineering/drafting scenario.

Of course, this gets complicated for actual floating-point numbers, since the number you're trying to round to probably isn't exactly representable in floating-point math, and floating-point numbers are not uniformly distributed...
Topic archived. No new replies allowed.