为什么C浮点类型会在输出时修改125.1到125.099998的实际输入?

我写了以下程序:

#include int main(void) { float f; printf("\nInput a floating-point no.: "); scanf("%f",&f); printf("\nOutput: %f\n",f); return 0; } 

我在Ubuntu上并使用GCC编译上述程序。 这是我想要查询的示例运行和输出:

 Input a floating-point no.: 125.1 Output: 125.099998 

为什么精度会发生变化?

因为数字125.1 不可能完全用浮点数表示 。 这在大多数编程语言中都会发生 使用例如printf("%.1f", f); 如果要打印带有一位小数的数字,但要注意:数字本身并不完全等于125.1。

谢谢大家的答案。 虽然几乎所有人都帮我看了正确的方向但我无法理解这种行为的确切原因。 所以除了阅读你们给我指的那些页面之外,我还做了一些研究。 以下是我对此行为的理解:

单精度浮点数通常使用4个字节存储在x86 / x86-64体系结构上。 然而,并非所有32位(4字节= 32位)都用于存储数字的大小。

对于作为单精度浮点类型存储,输入流的格式如下(有点类似于科学记数法):

 (-1)^sx 1.mx 2^(e-127), where s = sign of the number, range:{0,1} - takes up 1 bit m = mantissa (fractional portion) of the number - takes up 23 bits e = exponent of the number offset by 127, range:{0,..,255} - takes up 8 bits 

然后存储在内存中

 0th byte 1st byte 2nd byte 3rd byte mmmmmmmm mmmmmmmm emmmmmmm seeeeeee 

因此,十进制数125.1首先被转换为二进制forms,但限于24位,因此尾数不超过23位。 转换为二进制forms后:

 125.1 = 1111101.00011001100110011 

注意: 十进制0.1可以表示二进制的无限位,但计算机将表示限制为17位,因此完整表示不超过24位。

现在将其转换为指定的表示法,我们得到:

 125.1 = 1.111101 00011001100110011 x 2^6 = (-1)^0 + 1.111101 00011001100110011 x 2^(133-127) 

这意味着

 s = 0 m = 11110100011001100110011 e = 133 = 10000101 

因此,125.1将存储在内存中:

 0th byte 1st byte 2nd byte 3rd byte mmmmmmmm mmmmmmmm emmmmmmm seeeeeee 00110011 00110011 11111010 01000010 

在传递给printf()函数时,通过将二进制forms转换为十进制forms来生成输出流。 字节实际上以相反的顺序(来自输入流)存储,因此按以下顺序读取:

 3rd byte 2nd byte 1st byte 0th byte seeeeeee emmmmmmm mmmmmmmm mmmmmmmm 01000010 11111010 00110011 00110011 

接下来,它将转换为特定的转换表示法

 (-1)^0 + 1.111101 00011001100110011 x 2^(133-127) 

进一步简化上述表示:

 = 1.111101 00011001100110011 x 2^6 = 1111101.00011001100110011 

最后将其转换为十进制:

 = 125.0999984741210938 

但单精度浮点最多只能表示6位小数,因此答案四舍五入为125.099998

首先考虑一个固定点表示。

2^3=8 2^2=4 2^1=2 2^0=1 2^-1=1/2 2^-2=1/4 2^-3=1/8 2^-4=1/16

如果我们想要表示一个分数,那么我们将这些位设置在该点的右侧,因此5.5表示为01011000

但是如果我们想要代表5.6 ,那么就没有精确的分数表示。 我们最接近的是01011001 == 5.5625

1/2 + 1/16 = 0.5625

2^-4 + 2^-1

因为它是125.1的最接近的表示,所以请记住单精度浮点只是32位。

如果我告诉你把1/3作为十进制数字写下来,你会发现有一些没有有限表示的数字。 .1是1/10的精确表示,这个问题没有出现,但这只是十进制表示。 在二进制表示中.1是需要无限数字的那些数字之一。 因为你的号码必须减少,所以有些东西会丢失。

没有浮点数具有精确的表示,它们都具有有限的精度。 当从文本中的数字转换为浮点数(使用scanf或其他方式)时,您处于另一个具有不同数字类型的世界中,并且精度可能会丢失。 从float转换为字符串时也是如此:你决定你想要多少位数。 在转换为文本或其他可以保留该信息的格式之前,您无法知道浮点数中的“有多少位数”。 这一切都与浮动的存储方式有关 :

significant_digits * base exponent

用于C中浮点的常规类型是double,而不是float。 你的浮动被隐式地转换为double,并且因为浮点数不太精确,与最接近的可表示数字到125.1的差异更明显(并且printf的默认精度是为双打使用而定制的)。 试试这个:

 #include int main(void) { double f; printf("\nInput a floating-point no.: "); scanf("%lf",&f); printf("\nOutput: %f\n",f); return 0; }