简体   繁体   English

使C浮点数精确吗?

[英]Making C floats precise?

In C, when a float is set, 在C语言中,设置浮点数后,

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. 同样,例如,如果要表示以3为底的数字,则十进制是不精确的。 There's no exact binary representation of 0.1 decimal, just as there's no exact decimal representation of "one third". 没有精确的二进制表示形式为0.1十进制,就像没有精确的十进制表示形式为“三分之一”一样。

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. 对于精确的十进制值,最好使用第三方库...或保留一个逻辑上已知可以按100或10,000或其他比例缩放的整数。

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. 二进制浮点数以2的幂表示,而十进制数以10的幂表示,并且C的float无法完全表示所有十进制数。

Your example number, 96.1 can be written as: 您的示例号码96.1可以写为:

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:

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中表示0.1是有问题的。二进制文件中前几个小数位的位数是:

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. 因此,您需要以某种方式使用这些位置值的组合来将十进制的值总计大约为0.1。 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. 因此,您必须以b0.0001(d0.0625)作为小于d0.1的第一个位置,并添加更多的较小位置值来使其越来越接近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 使用float但请注意其局限性,并仔细设计算法以最大程度地减少舍入误差
  • Use an exact decimal representation such as BCD (binary coded decimal), which is used in financial systems to avoid rounding errors 使用精确的十进制表示形式,例如BCD(二进制编码的十进制),该形式在金融系统中用于避免舍入错误
  • 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. 问题是32位浮点不能精确表示96.6周期。

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. 它依赖于平台,但是通常无法准确表示98.6之类的数字。

What you can do is use printf precision specifiers like "%.2f" to "round" the number displayed. 您可以做的是使用诸如"%.2f"类的printf精度说明符来“舍入”所显示的数字。

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. 在您的示例中,尝试使用%f2.1(或类似格式)在小数点右边打印少量的数字。

The give away is the word float . 赠与是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). 该值是它可以在有限的位数(例如pi)中获得的最佳表示。

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/... 更好地使用整数并以美分/便士/ ...来计算...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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