4

可能重复:
浮点比较

我对 C/C++ 中浮点数的准确性有疑问。当我执行下面的程序时:

#include <stdio.h>

int main (void) {
    float a = 101.1;
    double b = 101.1;
    printf ("a: %f\n", a);
    printf ("b: %lf\n", b);
    return 0;
}

结果:

a: 101.099998
b: 101.100000

我相信 float 应该有 32 位所以应该足以存储 101.1 为什么?

4

6 回答 6

15

您只能在 IEEE754 中精确地表示数字(至少对于单精度和双精度二进制格式),如果它们可以通过将两个的倒幂(即,如、、等)相加来构造,则取决于可用的位数为了精确。2-n11/21/41/65536

在浮点数(23 位精度)或双精度数(52 位精度)提供的缩放比例范围内,没有两个的倒幂组合可以让您精确到 101.1 。

如果您想要快速教程了解这种倒置的二次幂的工作原理,请参阅此答案

将该答案中的知识应用于您的101.1数字(作为单精度浮点数):

s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm    1/n
0 10000101 10010100011001100110011
           |  | |   ||  ||  ||  |+- 8388608
           |  | |   ||  ||  ||  +-- 4194304
           |  | |   ||  ||  |+-----  524288
           |  | |   ||  ||  +------  262144
           |  | |   ||  |+---------   32768
           |  | |   ||  +----------   16384
           |  | |   |+-------------    2048
           |  | |   +--------------    1024
           |  | +------------------      64
           |  +--------------------      16
           +-----------------------       2

其中的尾数部分实际上会永远持续下去101.1

mmmmmmmmm mmmm mmmm mmmm mm
100101000 1100 1100 1100 11|00 1100 (and so on).

因此,这不是精度问题,没有多少有限位可以准确地以 IEEE754 格式表示该数字。

使用这些位计算实际数字(最接近的近似值),符号为正。指数为 128+4+1 = 133 - 127 偏差 = 6,因此乘数为 2 6或 64。

尾数由 1(隐式基数)加上(对于所有这些位,每个位值 1/(2 n ),因为 n 从 1 开始并向右增加){1/2, 1/16, 1/64, 1/1024, 1/2048, 1/16384, 1/32768, 1/262144, 1/524288, 1/4194304, 1/8388608},。

当你把所有这些加起来时,你得到1.57968747615814208984375.

当您将其乘以先前计算的乘数时64,您会得到101.09999847412109375

所有数字均bc使用 100 位十进制数字的比例计算,导致大量尾随零,因此数字应该非常准确。双重如此,因为我检查了结果:

#include <stdio.h>
int main (void) {
    float f = 101.1f;
    printf ("%.50f\n", f);
    return 0;
}

给了我101.09999847412109375000...

于 2012-09-28T07:38:41.213 回答
4

您需要阅读有关浮点数如何工作的更多信息,尤其是关于可表示数字的部分。

对于为什么您认为“32 位对于 101.1 应该足够”,您没有给出太多解释,因此很难反驳。

二进制浮点数不适用于所有十进制数,因为它们基本上存储数字,等待它,以 2 为底。就像二进制一样。

这是一个众所周知的事实,这就是为什么永远不应该用浮点数处理货币的原因。

于 2012-09-28T07:33:53.603 回答
4

您在 base 中的号码101.1在base10中。该部分正在重复。因此,无论您有多少位数,数字都无法在计算机中准确表示。1100101.0(0011)20011

查看IEE754浮点标准,您会发现为什么该double版本似乎完全显示了它。

PS: in base 的推导101.1101100101.0(0011)base 2

101 = 64 + 32 + 4 + 1
101 -> 1100101

.1 * 2 =  .2 -> 0
.2 * 2 =  .4 -> 0
.4 * 2 =  .8 -> 0
.8 * 2 = 1.6 -> 1
.6 * 2 = 1.2 -> 1
.2 * 2 =  .4 -> 0
.4 * 2 =  .8 -> 0
.8 * 2 = 1.6 -> 1
.6 * 2 = 1.2 -> 1
.2 * 2 =  .4 -> 0
.4 * 2 =  .8 -> 0
.8 * 2 = 1.6 -> 1
.6 * 2 = 1.2 -> 1
.2 * 2 =  .4 -> 0
.4 * 2 =  .8 -> 0
.8 * 2 = 1.6 -> 1
.6 * 2 = 1.2 -> 1
.2 * 2 =  .4 -> 0
.4 * 2 =  .8 -> 0
.8 * 2 = 1.6 -> 1
.6 * 2 = 1.2 -> 1
.2 * 2....

PPS:如果您想准确存储1/3in base的结果,也是一样的10

于 2012-09-28T07:39:15.697 回答
2

如果您的打印有更多数字,double您会发现甚至double无法准确表示:

 printf ("b: %.16f\n", b);

 b: 101.0999999999999943

事情是float并且double正在使用二进制格式,并不是所有的浮点数都可以用二进制格式精确表示。

于 2012-09-28T07:34:01.047 回答
2

您在这里看到的是两个因素的组合:

  • IEEE754 浮点表示不能准确表示一整类有理数和所有无理数
  • printf. 也就是说,使用 a 时的错误double发生在第 6 个 DP 的右侧某处。
于 2012-09-28T07:40:04.913 回答
1

不幸的是,大多数十进制浮点数不能用(机器)浮点精确表示。这就是事情的运作方式。

例如,二进制中的数字 101.1 将表示为1100101.0(0011)(该0011部分将永远重复),因此无论您必须存储多少字节,它都不会变得准确。是一篇关于浮点数的二进制表示的小文章,在这里您可以找到一些将浮点数转换为二进制的示例。

如果您想了解有关此主题的更多信息,我可以向您推荐这篇文章,尽管它很长而且不太容易阅读。

于 2012-09-28T07:37:24.400 回答