简体   繁体   中英

Making C floats precise?

In C, when a float is set,

int main(int argc, char *argv[]) {
    float temp = 98.6f;
    printf("%f\n", temp);
    return 0;
}

It always seems to get some kind of rounding error,

98.599998

But when I make it more precise,

float temp = 96.600000f;

It still prints a different number. How is this supposed to be solved?

It still prints a different number. How is this supposed to be solved?

By using a different data type, if you want precise decimal values.

Binary floating point numbers are precise - it's just they're precise binary values.

Likewise decimal is imprecise if you want to represent numbers in base 3, for example. There's no exact binary representation of 0.1 decimal, just as there's no exact decimal representation of "one third".

It's all a matter of working out what your requirements are, and using a data type which matches them. For precise decimal values, you're probably best off using a third-party library... or keep an integer that you know is logically scaled by 100, or 10,000 or whatever.

This is a fundamental limitation of representing decimal numbers in binary form. Binary floating point numbers are expressed in powers of 2 while decimal numbers are expressed in powers of 10, and C's float is simply unable to exactly represent all the decimal numbers.

Your example number, 96.1 can be written as:

96.1 = 9*10^1 + 9*10^0 + 1*10^-1

To represent this in binary, you can get the integer 96 just fine:

96 = 1*2^6 + 1*2^5

but representing the 0.1 is problematic in base 2. The place values of the first few fractional places in binary are:

2^-1 = 0.5
2^-2 = 0.25
2^-3 = 0.125
2^-4 = 0.0625
2^-5 = 0.03125
2^-6 = 0.015625
2^-7 = 0.0078125
2^-8 = 0.00390625
2^-9 = 0.001953125
... and so on

So somehow you need to use a combination of these place values to add up to approximate 0.1 in decimal. So you would have to start with b0.0001 (d0.0625) as being the first place less than d0.1 and add some more of the smaller place values to get closer and closer to 0.1. For example:

b0.001      = d0.125      // too high, try lower
b0.0001     = d0.0625     // too low, so add smaller places
b0.00011    = d0.09375    // good, closer... rounding error is 0.0625
b0.000111   = d0.109375   // oops, a little high
b0.00011001 = d0.09765625 // getting better - how close do you need?
...

And so on - you get the idea. So the binary values can only approximate decimals due to the fundamental representation.

There are many articles on floating point rounding errors and representational limits. It is definitely worth doing some background reading on this topic.

There are a few ways to solve this problem:

  • Use float but remain aware of the limitations and carefully design the algorithm to minimise rounding errors
  • Use an exact decimal representation such as BCD (binary coded decimal), which is used in financial systems to avoid rounding errors
  • Use a fixed data type, where numbers are expressed as fractions of integers, and only convert to floating point at the end of the calculation to display the result.

Adding trailing zeroes will never make a difference.

The problem is that 32-bit floating cannot precisely express 96.6 , period.

It isn't randomly picking digits to fill what you left out; it's rounding it to the closest number that it can express.

It's platform-dependent, but usually a number like 98.6 can't be represented exactly.

What you can do is use printf precision specifiers like "%.2f" to "round" the number displayed.

There's no simple answer. It has to do with how floats are represented in memory, but how we tend to think they can represent all real numbers. They can't. If you want to be precise with floats, think of less-than or greater-than ranges instead of trying to equate them. In your example, try %f2.1 (or similar) to print out a smaller amount of digits to the right of the decimal place.

The give away is the word float .

It is composed of a mantissa and an exponent. The value is the best representation that it can achieve in a limited number of bits (take pi for example).

So do not use equality as you get rounding errors. You can take steps to minimise them, but that requires a few lectures and a text book.

BTW - Do not use floats for money. Better use integers and compute things in cents/pennies/...

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