简体   繁体   中英

Rounding floats in C

While testing the float type and printing it with it's format specifier %f I was testing it's rounding methods.

I've declared the variable as float and gave it the value 5.123456. As you know float must represent at least 6 significant figures.

I then changed it's value to 5.1234567 and printed the value with the %f . It baffles me why it prints out as 5.123456. But if I change the variable value to 5.1234568, it prints out as 5.123457. It rounds properly.

If I haven't made myself clear or the explanation is very confusing:

float a = 5.1234567
printf("%d", a); 
// prints out as 5.123456

float a = 5.1234568
printf("%d", a); 
// prints out as 5.123457

I've compiled using CodeBlocks and MinGW, same result.

OP is experiencing the effects of double rounding

First, the values 5.123456, 5.1234567, etc. are rounded by the compiler to the closest representable float . Then printf() is rounding the float value to the closest 0.000001 decimal textual representation.


I've declared the variable as float and gave it the value 5.123456. As you know float must represent at least 6 significant figures.

A float can represent about 2^32 different values. 5.123456 is not one of them. The closest value a typical float can represent is 5.12345600128173828125 and that is correct for 6 significant digits: 5.12345...

float x = 5.123456f;
//  5.123455524444580078125 representable float just smaller than 5.123456
//  5.123456                OP's code
//  5.12345600128173828125  representable float just larger  than 5.123456 (best)

// The following prints 7 significant digits
// %f prints 6 places after the decimal point.
printf("%f", 5.123456f); // --> 5.123456

With 5.1234567, the closest float has an exact value of 5.123456478118896484375. When using "%f" , this is expected print rounded to the closest 0.000001 or 5.123456

float x = 5.1234567f;
//  5.123456478118896484375 representable float just smaller than 5.1234567 (best)
//  5.1234567                OP's code
//  5.1234569549560546875   representable float just larger  than 5.1234567

// %f prints 6 places after the decimal point.
printf("%f", 5.1234567f); // --> 5.123456

Significant digits is not the number of digit after the decimal point. It is the number of digits starting with the left-most (most significant) digit.

To print a float to 6 significant figures, use "%.*e" .
See Printf width specifier to maintain precision of floating-point value for more details.

float x = 5.1234567;         
printf("%.*e\n", 6 - 1, x);  // 5.12346e+00
                             // x xxxxx   6 significant digits

There is no exact float representation for the number 5.1234567 you intend to show here.

If you check here: https://www.h-schmidt.net/FloatConverter/IEEE754.html

You can see that this number is converted into 5.1234565, or the double 5.1234564781188965 and this rounds down,

While the number 5.1234568 is representable in float, and has a double representation of 5.123456954956055, and this rounds up.

There are two levels of rounding going on:

  1. Your constant of 5.1234567 gets rounded to the nearest value which can be represented by a float (5.123456478...).
  2. The float gets rounded to 6 digits when printed.

It will become obvious if you print the value with more digits.

What it comes down to is that the mantissa of a float has 23 bits and this is not the same as 6 decimal digits (or any number of digits really). Even some apparently simple values like 0.1 don't have an exact float representation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM