浮点数

February 3, 2021
计算机基础

基本理论:

在计算机中我们使用补码对整数进行编码,对于小数,由于小数点的位置是不确定的,不能和整数使用相同的编码方式,所以引入了新的编码方式”浮点数”。 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

计算机基础单位

计算机基础