C浮点精度

时间:2012-09-28 07:30:38

标签: c++ c floating-point floating-point-conversion

  

可能重复:
  Floating point comparison

我对C / C ++中float的准确性有疑问。当我执行以下程序时:

#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

我认为浮点数应该是32位,所以应该足以存储101.1为什么?

6 个答案:

答案 0 :(得分:11)

如果可以通过将两个反转幂相加(即2-n类似1来构造,则只能在IEEE754中精确表示数字(至少对于单精度和双精度二进制格式)) 1/21/41/65536等等,取决于可用于精确度的位数。

没有两个反转功率的组合可以精确到101.1,在浮点数(23位精度)加倍(52位精度)提供的缩放范围内。

如果您想快速了解这两种倒置功能的工作原理,请参阅this answer

将该答案中的知识应用于您的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 bias = 6,因此乘数为2 6 或64。

尾数由1(隐式基数)加上(对于所有这些位,每个值为1 /(2 n ),因为n从1开始并向右增加),{{ 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使用100位十进制数字进行计算,从而产生大量尾随零,因此数字非常准确。因此,我检查了结果:

bc

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

答案 1 :(得分:4)

您需要详细了解how floating-point numbers work,尤其是representable numbers上的部分。

你没有给出很多解释,为什么你认为“32位应该足够101.1”,所以很难反驳。

二进制浮点数对所有十进制数都不适用,因为它们基本上存储数字,等待它,基数为2.如二进制数。

这是一个众所周知的事实,这就是为什么这样做的原因。金钱永远不应该以浮点来处理。

答案 2 :(得分:4)

基数101.1中的101100101.0(0011)位于基座20011double部分正在重复。因此,无论您拥有多少位数,该数字都无法在计算机中准确表示。

查看浮点数的IEE754标准,您可以找出101.1版本似乎完全显示的原因。

PS:基础10中基于1100101.0(0011)的{​​{1}}的推导为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/3的结果准确存储在基座10中,则相同。

答案 3 :(得分:2)

如果double的打印位数更多,您会发现即使double无法准确表示:

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

 b: 101.0999999999999943

事情是floatdouble使用二进制格式,并非所有浮动指针编号都可以用二进制格式精确表示。

答案 4 :(得分:2)

您在此处看到的是两个因素的组合:

  • IEEE754浮点表示不能准确表示一整类理性数和所有无理数
  • printf中舍入(默认为6位小数)的效果。也就是说使用double时出现的错误发生在第6个DP的右侧。

答案 5 :(得分:1)

不幸的是,大多数十进制浮点数无法在(机器)浮点中准确表示。这就是事情的运作方式。

例如,二进制数101.1将表示为1100101.0(0011)0011部分将永远重复),因此无论您需要存储多少字节,它都不会成为准确。 Here是一篇关于浮点二进制表示的文章,here你可以找到一些将浮点数转换为二进制数的例子。

如果您想了解有关此主题的更多信息,我可以向您推荐this article,虽然它很长且不太容易阅读。