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

Har*_*han 1 c floating-point

我写了以下程序:

 #include<stdio.h>
    int main(void)
    {
     float f;
     printf("\nInput a floating-point no.: ");
     scanf("%f",&f);
     printf("\nOutput: %f\n",f);
     return 0;
    }
Run Code Online (Sandbox Code Playgroud)

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

Input a floating-point no.: 125.1
Output: 125.099998
Run Code Online (Sandbox Code Playgroud)

为什么精度会发生变化?

Aas*_*set 12

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


Har*_*han 6

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

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

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

(-1)^s x 1.m x 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
Run Code Online (Sandbox Code Playgroud)

然后存储在内存中

0th byte 1st byte 2nd byte 3rd byte
mmmmmmmm mmmmmmmm emmmmmmm seeeeeee
Run Code Online (Sandbox Code Playgroud)

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

125.1 = 1111101.00011001100110011
Run Code Online (Sandbox Code Playgroud)

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

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

125.1 = 1.111101 00011001100110011 x 2^6
      = (-1)^0 + 1.111101 00011001100110011 x 2^(133-127)
Run Code Online (Sandbox Code Playgroud)

这意味着

s = 0
m = 11110100011001100110011
e = 133 = 10000101
Run Code Online (Sandbox Code Playgroud)

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

0th byte 1st byte 2nd byte 3rd byte
mmmmmmmm mmmmmmmm emmmmmmm seeeeeee
00110011 00110011 11111010 01000010
Run Code Online (Sandbox Code Playgroud)

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

3rd byte 2nd byte 1st byte 0th byte
seeeeeee emmmmmmm mmmmmmmm mmmmmmmm
01000010 11111010 00110011 00110011
Run Code Online (Sandbox Code Playgroud)

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

(-1)^0 + 1.111101 00011001100110011 x 2^(133-127)
Run Code Online (Sandbox Code Playgroud)

在进一步简化上述表示时:

= 1.111101 00011001100110011 x 2^6
= 1111101.00011001100110011
Run Code Online (Sandbox Code Playgroud)

最后将其转换为十进制:

= 125.0999984741210938
Run Code Online (Sandbox Code Playgroud)

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