我想知道编译器显示float 值不准确的机制。实施例
float a = 0.056;
printf("value = %f",a); // this prints "value = 0.056"
如果您尝试以二进制浮点格式存储0.056,则可以得到此值(use this link for conversion)
0.00001110010101100000010000011000,等于0.0559999998658895
1。编译器如何显示0.056,同时显示0.055999999?
让我们再看一下这个例子
#include <stdio.h>
main()
{
float a, b;
a = 0.056;
b = 0.064; // difference is 0.08
printf("a=%f, b=%f",a,b);
if( b - a == 0.08) // this fails
printf("\n %f - %f == %f subtraction is correct",b,a,b-a);
else
printf("\n%f - %f != %f Subtraction has round-off error\n",b,a,b-a);
}
请注意,else块在此处执行,而我们期望块是正确的。这是输出。
a=0.056000, b=0.064000
0.064000 - 0.056000 != 0.008000 Subtraction has round-off error
同样,值以我们期望的方式显示(没有舍入错误),但这些值确实具有舍入错误,但显示了错误的伪装值。我的第二个问题是
2。有没有办法显示存储号码的实际价值而不是我们输入的伪装号码?
注意:我在Visual Studio 2008中包含了C代码,但它应该可以用任何语言重现。
答案 0 :(得分:11)
编译器没有显示任何内容?您的程序显示0.056,因为%f
仅显示最多6位数的结果。如果您想查看所有不准确之处,请尝试%.16f
(结果:http://ideone.com/orrkk)。
manpage of printf显示了许多可以与这些说明符一起使用的其他选项。
答案 1 :(得分:4)
在大多数语言中,用于打印浮点值的例程实际上会打印最接近要打印的浮点值的最小十进制数,而不是任何其他浮点值。这通常(但不总是)掩盖了将十进制文字转换为浮点值所导致的舍入误差。
答案 2 :(得分:2)
我看到很多人都在谈论printf
以及它是如何以“错误的方式”打印东西,因为它会使事情变得圆满,等等。printf
正在打印正是您所期望的{ em>,当您发现a
中存储的实际数字是 0.05600000172853469848
时。
OP假设存储在那里的数字是0.0559999...
,但是查看实际数字会显示错误:
#include <stdio.h>
int main() {
float a = 0.056;
printf("%A\n", a);
}
这将打印0X1.CAC084P-5
,这意味着我们的尾数(0xCAC084
)为110010101100000010000100
。那是24位,而不是23位我们可以存储在32位(IEEE-754单精度)浮点中,这意味着那里的内容实际上是11001010110000001000010
请记住,尾数是标准化的,并假设从1
开始,因此,应用指数等,我们的数字是:
0.0000111001010110000001000010
转换为0.05600000172853469848
OP假定这一点,而不是:
0.00001110010101100000010000011
当然更准确,但这需要比尾数可以存储的更多,所以我们最终得到这个:
0.0000111001010110000001000001
或0.05599999800324440002
。
当然,没有数字是0.056
,但后者的表示错误更高!所以我们得到的东西就不足为奇了......
答案 3 :(得分:1)
2)如果您有C99库,请尝试以十六进制打印双倍
printf("%A\n", 56.0/100);
答案 4 :(得分:1)
你错误地认为你的浮动曾准确。
它们并非旨在表示0.0559999998658895
等精确值。像GMP这样的库存在。
浮动设计快速且近似。
在您的示例中,会显示0.056
,因为数字0.0559999
被认为是准确的,而99865889...
后面的数字主要被认为是噪音而且只有足够大的数字才能整齐0.0559999
1}}到0.056
。
printf
并不知道您认为0.056
是“正确的”。它只知道以人类可读格式打印的浮点数仅精确到大约6位有效数字,0.0560000
表示使用那么多位数的最接近匹配。