基本理论:
在计算机中我们使用补码对整数进行编码,对于小数,由于小数点的位置是不确定的,不能和整数使用相同的编码方式,所以引入了新的编码方式”浮点数”。
C 语言中使用 1B 的空间可以表示整数0——255的数字, 为其赋值 256 就会导致溢出。 同样浮点数也是有范围的, 浮点数也有不能表示的数字,例如 0.1, 0.2, 无限小数,16777217.0等。
和整数一样,当我们把一个超出表示范围的数字赋值给一个 float 类型时,需要进行舍入。
有四种舍入方式(截图自维基百科):
C 语言中的舍入规则是什么?
void show_fe_current_rounding_direction()
{
printf("current rounding direction: ");
switch (fegetround()) {
case FE_TONEAREST: printf ("FE_TONEAREST"); break;
case FE_DOWNWARD: printf ("FE_DOWNWARD"); break;
case FE_UPWARD: printf ("FE_UPWARD"); break;
case FE_TOWARDZERO: printf ("FE_TOWARDZERO"); break;
default: printf ("unknown");
};
printf("\n");
}
运行上面的程序我们可以知道 C 语言的默认舍入规则是 FE_TONEAREST。
二进制浮点数 <=> 十进制 转换规则
浮点数的二进制表示:
十进制到浮点数的转换公式:
num=(-1)^s *(1+M)* 2^(E-127)(单精度)
num=(-1)^s *(1+M)* 2^(E-1023)(双精度)
转换实例:
一个例子:
#include <stdio.h>
#include <float.h>
#include <limits.h>
int main()
{
float f1=16777216.0;
float f2=16777217.0;
float f3=1677218.0;
printf("16,777,216:%f\n",f1);
printf("16,777,217:%f\n",f2);
printf("16,777,218:%f\n",f3);
printf("f1==f2?%s",f1==f2?"true":"false");
return 0;
}
运行结果如下:
我们发现 f1 == f2, 这显然是超出我们的预期的。
让我们一步一步来看原因。
我们使用浮点数转换规则进行如下操作:
f1 = (-1)^0 * 2^24 * (1+0)
f2 = (-1)^0 * 2^24 * (1+0.000000059604645)
f3 = (-1)^0 * 2^24 * (1+0.00000011920929)
所以,符号位S = 0, 阶码E = 24 + 127 = 151 = 10010111b
f1 = 01001011100000000000000000000000
f2 = 010010111000000000000000000000001...
f3 = 01001011100000000000000000000001
因为 float 只有 32 bit,根据 FE_TONEAREST 舍入后的结果是:
f1 = 01001011100000000000000000000000
f2 = 01001011100000000000000000000000
f3 = 01001011100000000000000000000001
所以就会出现上面的 f1 == f2。
参考:
1.2浮点数表示实验 —— 《计算机组成原理实践教程——从逻辑门到CPU》
实验四 浮点数表示实验 —— 课后题答案
[Math] IEEE 754 round-off errors
为什么 0.1 + 0.2 = 0.300000004
在线进制转换
浮點數的捨入 —— IEEE 754 —— 维基百科
IEEE-754 Floating Point Converter