引子
有一个C程序的例子:
#include <stdio.h>
int main(){
float a = 16777217.0f;
if(a == 16777216.0f) // 这里判断应该为假?
printf("value is %.10fn", a);
if(0.1 + 0.2 == 0.3) // 这里判断应该为真?
printf("you will see: 0.1 + 0.2 == 0.3n");
int x = 65535;
int y = x*x;
printf("x * x = %dn", y); // 这里应该打印4294836225?
x = -2147483648;
// 这里应该打印true?
printf("x: %d and y: %d; (x>y) == (-x<-y) is %sn", -2147483648, 1, (x>1) == (-x<-1)?"true": "false");
return 0;
}
实验结果是:
value is 16777216.0000000000
x * x = -131071
x: -2147483648 and y: 1; (x>y) == (-x<-y) is false
要理解为什么输出结果与我们预期的不符应该理解本节所讲的内容。
定点表示法没有办法有效地表示一个很大的数。5*2^100的二进制表示方式是10100...0(Binary103位),而显然我们不希望这样表示一个很大的数。因此我们使用IEEE浮点标准来表示大的数字或者小数。
IEEE浮点标准使用类似科学计数法的方式来定义:
- s 表示符号(sign),决定了这个数是不是整数
- M表示尾数(mantissa),是一个二进制小数
- E 表示阶码(exponent),表示对浮点数的加权平均,权重是2的E次幂,可以是负数。
一般计算机用IEEE 754来表示小数。
规格化数
- 规格化数
在二进制小数中,若将其表示为类似科学计数法的形式,尾数部分的第一个有效位一定为1,为了尽可能多的表示数字,我们规定单精度的Significand部分表示1.xxxxxx尾数的xxxx部分。 - 符号
1 represents negative and 0 represents positive - 阶数
- 范围从
0000 0001(该阶码的值为:-126) ~ 1111 1110(127) - 全0和全1表示特殊的数
- 单精度(single precision)偏置常数127(不是标准的偏置常数)
- 双精度(double precision)偏置常数1023(2^10 - 1)
- 范围从
使用127与1023而非128&1024的原因是:
1. 阶码的上界可以更大,表示的范围更广,而实际上选哪个都是可以的。
2. 后面计算最大 非规格化数与最小 规格化数时可以发现,这样设置偏置常数有利于这两个连续数字的 平滑转变(相邻两个数在数轴上的距离不会太远)
两个概念:
阶是用移码表示的,里面有几个值需要说明:
1. 阶码:0000 0001B = 1
2. 阶码的值:1 - 127 = -126阶码表示机器保存的数字,按原码读法读出来的值 阶码的值表示这个数字按照实际的移码读法读出来的值,这个值也是实际算十进制真值时的指数的值
- 尾数
- 规格化尾数最高位是1,隐含表示;实际尾数表示1后的部分
- 单精度(single precision)尾数可表示:1 + 23bits
- 双精度(double precision)尾数可表示:1 + 52bits
非规格化数
如下表
关于无穷大,可以利用无穷大作比较,X / 0 > Y其中X、Y为浮点数
关于无穷大和NaN的运算
5.0/0 = +∞; -5.0/0 = -∞;5+(+∞) = +∞; 5-(+∞) = -∞;0/0 = NaN; sqrt(-4.0) = NaN;op(NaN, x) = NaN; (+∞)+(-∞) = NaN;∞/∞ = NaN; (+∞)-(+∞) = NaN;
我们还记得,在规格化数中,尾数表示1.xxxxx,是隐含了最高位为1的。那么对于阶数为0,尾数为非0的非规格化数来说,这个尾数表示的则是0.xxxxx。非规格化数用来应对算数下溢的问题,它提供了一些数来尽可能表示原先算数下溢的数字,以求精确。
总的来说,非规格化数的作用:
- 提供了表示数值0的方法。IEEE的标准中,值+0.0 和 -0.0 某些方面是不同的(比如阐述负数算数下溢时),而其他方面是相同的(在浮点数比较中,
-0.0 == +0.0)。 - 表示那些非常接近0.0的数,非规格化数的存在使得数的表示可以渐近下溢(下面将会提到)。
- 对于NaN这样的特殊值,用来表达一些运算得到不能是实数也不能是无穷的结果,例如对-1开根号;某些情况下,用来表示未初始化的数据。
浮点数的溢出
浮点数的溢出同定点数溢出一样,都表示计算产生出来的结果是非常大的,大于寄存器或存储器所能存储或表示的能力限制。
在浮点数中,对于算数上溢,我们用无穷来表示,即
对于算数下溢来说,表示现在计算结果很接近零,使得计算结果的大小小于浮点数可以表示的最小数字。
界于−fminN and fminN之间的区间称为下溢间距(underflow gap),其中fminN为一般浮点数格式所能表示的最小正数。
在早期的设计中,界于下溢间距之间的数字其值均视为零,因此若出现算术下溢,其结果会被改为零,可能是用硬件或系统软件处理,此处理方式称为“清洗为0”(flush to zero)。
而IEEE引入非正规数后,使用非正规数和0,可以填满下溢间距。假设浮点数指数范围为-128至127,最小可表示正规数为
对于 渐进下溢(gradual underflow)和 突然式下溢出(abrupt underflow)的区别可以见这里的 非规约形式的浮点数章节
这一章我们介绍了广泛应用于编程语言小数的浮点数IEEE 754标准所定义的规格化数与非规格化数的含义,理解小数在计算机中的表示,这是我们解释浮点数在编程过程中遇到的各种问题时的基础。
这篇文章有诸多概念,我们可以结合下一篇文章的栗子和图文进行理解。
Yannick:Introduction to CSAPP(七)浮点数表示的直观例子zhuanlan.zhihu.com
1万+

被折叠的 条评论
为什么被折叠?



