C++ c中%f说明符的不可预测行为

C++ c中%f说明符的不可预测行为,c++,c,C++,C,代码如下: float f = 23.456; printf("%f", f); 不同f值的输出为: 为什么获取的值不可预测或存在模式?这是因为C使用模式来存储浮点数。您拥有的尾数转换为二进制,并存储在浮点变量所需的4字节空间的23位低位。有时它可以完全转换为精确的二进制,但有时它可能无法精确转换,所以会存储一个截断形式的数字,当从内存中读取时,其计算结果会略有不同 这种行为类似于在数学中,根据精度要求,使用1/3的值作为0.33或0.333 尝试使用: f=0.25;或 f=-15.625;

代码如下:

float f = 23.456;
printf("%f", f);
不同f值的输出为:

为什么获取的值不可预测或存在模式?

这是因为C使用模式来存储浮点数。您拥有的尾数转换为二进制,并存储在浮点变量所需的4字节空间的23位低位。有时它可以完全转换为精确的二进制,但有时它可能无法精确转换,所以会存储一个截断形式的数字,当从内存中读取时,其计算结果会略有不同

这种行为类似于在数学中,根据精度要求,使用1/3的值作为0.33或0.333

尝试使用: f=0.25;或 f=-15.625;或者,任何可以精确转换为二进制的十进制值,都会得到正确的结果。

这是因为C使用它来存储浮点数。您拥有的尾数转换为二进制,并存储在浮点变量所需的4字节空间的23位低位。有时它可以完全转换为精确的二进制,但有时它可能无法精确转换,所以会存储一个截断形式的数字,当从内存中读取时,其计算结果会略有不同

这种行为类似于在数学中,根据精度要求,使用1/3的值作为0.33或0.333

尝试使用: f=0.25;或
f=-15.625;或者,任何可以精确转换为二进制的十进制值,您都会得到正确的结果。

这属于每个程序员都应该知道的浮点类型。浮点数的工作方式意味着某些数字不能完全用浮点数表示。解释这一点的最简单方法是让您将值1/3记为十进制数。一开始你很开心,但最终你的纸用完了。这是因为在以10为基数的表示法中,数字1/3是无限长的,因此在使用以10为基数的表示法存储数字的合理编码系统中,数字的长度是有限制的

浮点也做同样的事情,但使用基数2。这意味着对我们来说似乎非常简单的数字,例如1/10或0.1,变成无限递归。当您打印数字时,这会导致舍入错误,原因很简单,因为您存储的数字不是您提供给编译器的数字,因为无法存储该数字


关于这一点的规范性论文是,这本书读得很透彻,但它解释了所有这些以及更多内容。

这属于每个程序员都应该知道的关于浮点的事情。浮点数的工作方式意味着某些数字不能完全用浮点数表示。解释这一点的最简单方法是让您将值1/3记为十进制数。一开始你很开心,但最终你的纸用完了。这是因为在以10为基数的表示法中,数字1/3是无限长的,因此在使用以10为基数的表示法存储数字的合理编码系统中,数字的长度是有限制的

浮点也做同样的事情,但使用基数2。这意味着对我们来说似乎非常简单的数字,例如1/10或0.1,变成无限递归。当您打印数字时,这会导致舍入错误,原因很简单,因为您存储的数字不是您提供给编译器的数字,因为无法存储该数字

关于这一点的规范性论文是,这是一篇很有分量的文章,但它解释了所有这些以及更多。

对于float或double,计算机中有一定数量的位来表示数字x。对于float,它通常是32位,double有64位

结果如何?只能在浮点数中存储2^32个不同的数字,在双精度中存储2^64个不同的数字。由于有无限多的其他数字,这意味着这些数字无法表示。但是人们仍然希望使用这些数字。一个关于数字不可用的编译器错误是奇怪的,不是吗?。因此,选择一个接近x的数字。在大多数应用程序中,这已经足够接近了。但出于同样的原因:永远不要相信浮点,也就是说,永远不要试图询问x=5?

使用浮点或双精度,您在计算机中有一定数量的位来表示您的数字x。对于float,它通常是32位,double有64位

结果如何?只能在浮点数中存储2^32个不同的数字,在双精度中存储2^64个不同的数字。由于有无限多的其他数字,这意味着这些数字无法表示。但是人们仍然希望使用这些数字。一个关于数字不可用的编译器错误是奇怪的,不是吗?。因此,选择一个接近x的数字。在大多数应用程序中,这已经足够接近了。但是出于同样的原因:永远不要信任浮点,也就是说,永远不要试图询问is x=5?

23.456不是浮点,它是一个双重使用的浮点f=23.456F编译器最有可能

除非您故意编译时没有警告,否则也会对此发出警告。您可以检查列表中的第三个吗?所有其他的我都完全期待,但第三个看起来是错误的。如果我的CRT打印了第三个,我会报告它是一个错误。@GrijeshChauhan:这不是OP问题的原因。这在stackoverflow上已经被问了上千次了。这是因为小数不能用二进制浮点精确表示。23.456不是浮点,它是一个双用浮点f=23.456F编译器很可能也会对此发出警告,除非您有意编译时没有警告。您能检查列表中的第三个吗?所有其他的我都完全期待,但第三个看起来是错误的。如果我的CRT打印了第三个,我会报告它是一个错误。@GrijeshChauhan:这不是OP问题的原因。这在stackoverflow上已经被问了上千次了。这是因为小数不能精确地用二进制浮点表示。具有超过23位二进制表示的数字会出现错误吗?是吗?所以输出应该取决于与否的接近程度。获取实际数字。但无论如何,数字都会更小,那么为什么printf中的值会更大?@user2653718当转换不精确时,将使用更接近的23位二进制表示,有时更高,有时更低。这里舍入可能意味着数字的二进制表示中缺少一些位,是吗?@chux thx.检查closer no.可能需要更改最后一位或其他方式?是的。具有超过23位二进制表示的数字会出现错误吗?是吗?所以输出应该取决于与否的接近程度。获取实际数字。但无论如何,数字都会更小,那么为什么printf中的值会更大?@user2653718当转换不精确时,将使用更接近的23位二进制表示,有时更高,有时更低。这里舍入可能意味着数字的二进制表示中缺少一些位,是吗?@chux thx.checking closer no.可能会涉及到更改最后一位或其他方式?我在这里遗漏了一点:在浮点的二进制表示中,存储的no.假定正数总是小于或等于实际数。因此将要进行的舍入将小于实际数。那么我们为什么要这样做呢从printf获取更大的值?@user2653718数字并不总是向下舍入。好的,选择最近的可表示数字,通常稍大一点。thx。最近的可表示23位数字可以通过更改最后一位或1-2位或其他方式获得?@user2653718:实际值下面总是有一个23位数字,上面总是有一个23位数字。它们通常相差1位,但在某些地方,指数会发生变化,最后一位的含义会发生2倍的变化。不管在这两者之间,选择了最接近的近似值。我在这里遗漏了一点:在浮点的二进制表示中,存储的数字假定为正数将始终小于或等于实际数字。因此,将要进行的舍入将小于实际数字。那么,为什么我们从printf获得更大的值?@user2653718数字并不总是向下舍入。好的,选择最近的可表示数字,通常稍大一点。thx。最近的可表示23位数字可以通过更改最后一位或1-2位或其他方式获得?@user2653718:实际值下面总是有一个23位数字,上面总是有一个23位数字。它们通常相差1位,但在某些地方,指数会发生变化,最后一位的含义会发生2倍的变化。无论如何,在两者之间选择最接近的近似值。浮点通常使用基数2。如果OP使用的FP是以10为基数的,肯定不常见,那就毫无疑问了。IEEE 754-2008指定了各种十进制FP格式,并怀疑我们将来会看到更多这种格式。@chux-你很正确,以10为基数的浮点值可以解决这个问题,但这只会引入进一步的问题float f=1.f/3.f;printf%f,f;把答案弄错了。总会有一些数字无法用计算机精确表示。浮点通常使用基数2。如果OP使用的FP是以10为基数的,肯定不常见,那就毫无疑问了。IEEE 754-2008指定了各种十进制FP格式,并怀疑我们将来会看到更多这种格式。@chux-你很正确,以10为基数的浮点值可以解决这个问题,但这只会引入进一步的问题float f=1.f/3.f;printf%f,f;把答案弄错了。总有一些数字不能用计算机精确地表示出来。
 Value       Output
---------    --------       
f = 23.456   23.455999 
f = 23.956   23.955999
f = 23.947   23.947001
f = 23.656   23.656000