CSAPP学习记录-IEEE浮点表示

使用二进制定点表示浮点数十分麻烦,且由于有些浮点数的二进制不能精确表示,只能添加小数位数不断近似,使得位数开销极大。20世纪70年代已经有一些计算机厂家推出了自己的浮点数表示标准,但并不统一,于是在1985年IEEE 742标准被提出,作为目前国际浮点数的表示标准。

IEEE 742标准规定了一个浮点数表示公式: V = ( − 1 ) s ∗ M ∗ 2 E V = (-1)^s * M * 2^E V=(1)sM2E

  • 怎么理解这个公式?

    其实这里有一个知识点:浮点数是用科学计数法来表示的。

    比如十进制数25.125

    25.12 5 ( D ) = 11001.00 1 B = 1.1001001 ∗ 2 4 25.125_{(D)} = 11001.001_{B} = 1.1001001 * 2^4 25.125(D)=11001.001B=1.100100124

    你也可以把它表示成 0.11001001 ∗ 2 5 0.11001001 * 2^5 0.1100100125, 11.001001 ∗ 2 3 11.001001 * 2^3 11.00100123, 110.01001 ∗ 2 2 110.01001 * 2^2 110.0100122, 11100100.1 ∗ 2 − 3 11100100.1 * 2^{-3} 11100100.123等等,实际上浮点数的得名也是源自于它的点位漂浮不定的情况,在实际应用中我们采用IEEE 标准,用科学计数法统一表示这些浮点数。

    回头看公式,其中V(Value)表示浮点数的十进制值;

    S(Sign)符号位表示浮点数的正符号,取1为负,0为正;上例显然是正数, S = 0。

    注意,面对这个符号位要摆脱之前整数补码取反加1求负的思想,实际上求浮点数的负值只要偏转一个符号位就行了。

    M(Mantissa)表示尾数,表示 二进制浮点数 科学计数法表示形式 下的小数部分; 上例中M = 1.1001001

    E (Exponent) 表示阶码,是一个有符号数,表示 二进制浮点数 科学计数法表示形式 的2次幂的值,上例中 E = 4

  • 怎么把数装进计算机的二进制位?

    ​ IEEE 提供了浮点数的32位和64位表示形式:

    image-20211029165937801

    这里除了符号位外又看到两个变量exp 和 frac 。

    这两个参数的作用是什么?这里又要开始讲IEEE标准,制定IEEE标准的这帮人根据exp的值分了三类情况:规格化,非规格化,特殊值情况,我们先主讲规格化的情况。

    • 规格化

      即exp的位模式既不全为0也不全为1的情况,该情况下IEEE二进制数对应到科学计数法表示要通过如下公式:

      E = e x p − b i a s E = exp - bias E=expbias

      M = 1 + f r a c M = 1 + frac M=1+frac

      exp是exponent,即指数,注意别和阶码E混淆,exp是一个无符号整数,而E是有符号整数;

      frac是fraction, 即M去掉整数部分后的小数字段, 注意规格化表示下尾数的第一位一定是1,正如科学计数法下非0数表示的第一位一定非0的情况。

      其中bias 是一个偏置值,和之前2.3用于保证整数向0舍入的bias不一样,这个bias是用于浮点数计算时能对齐两数的阶码使用的。这一部分我并不是了解的很清楚,目前要记住E是一个移码,参考:

      为什么要用移码来表示阶码(指数)呢?

      bias 的取值是 b i a s = 2 n − 1 − 1 bias = 2^{n-1} - 1 bias=2n11 ,其中 n 是exp的数据位数,单精度下bias 值为 127(n = 8), 双精度下为 1023 (n = 11)。

      单精度情况下,阶码E的有符号表示范围为-127 ~ +128 ,但是-127 和 128 在规格化里是没被用来表示的,原因是在IEEE标准中这两个数分别被用来规定了非规格化和特殊值的情况,也即单精度的规格化阶码exp范围是 00000001 ( − 126 ) − 11111110 ( 127 ) 00000001(-126)-11111110(127) 00000001(126)11111110(127) , 之后讨论。

      注意,看exp不能按正常的二进制转十进制的思维转成十进制,需要减去偏差。

      单精度情况exp和E对照:

      exp(B)exp(D)E
      0000 0000 (非规格化)0-126(非规格化情况E = 1 - bias)
      0000 00011-126
      1111 1110254127
      1111 1111 (特殊值)255

      规格化情况的公式变为:

      V = { ( − 1 ) s ∗ ( 1 + f r a c ) ∗ 2 e x p − 127 , f l o a t   ( − 1 ) s ∗ ( 1 + f r a c ) ∗ 2 e x p − 1023 , d o u b l e V = \begin{cases} (-1)^s * (1 + frac)*2^{exp - 127},float\\\ (-1)^s * (1 + frac)*2^{exp - 1023},double\end{cases} V={(1)s(1+frac)2exp127,float (1)s(1+frac)2exp1023,double

      按照规格表示,我们得到了25.125D(即 1.1001001 ∗ 2 4 1.1001001 * 2^4 1.100100124)的单精度表示方法:

      1 ( s , 1 位 ) 1000001 1 ( e x p , 8 位 ) 1001001000000000000000 0 ( f r a , 23 位 ) 1_{(s,1位)} 10000011_{(exp,8位)} 10010010000000000000000_{(fra,23位)} 1(s,1)10000011(exp,8)10010010000000000000000(fra,23)

      注意,在实际的位表示中开头1被省略掉了(即implied leading 1, 隐含的1开头表示),用以获得一个额外的尾数位以提高精度。

      • 非规格化

        阶码域全为0时,表示的值就是非规格化的,非规格数可以用来表示0及其他一些非常小的数,注意+0.0和-0.0在浮点数中是同时存在的,仅有符号位的不同。非规格化中M和E的公式定义为:

        M = f r a c M = frac M=frac

        E = 1 − b i a s E = 1- bias E=1bias

        非规格化的尾数是0开头的,故M代表的就是小数部分,至于E 为什么等于 1-bias会比较费解,其实这里的1是对非规格化数没有隐含的1的补偿,其有益于最大非规格化数到最小规格化数的平滑转变。

        另外,值比较小的规格化的数在更低位数的机器中可能成为非规格化数。我们知道,**增大尾数位有益于提高浮点数的表示精度从而得到更大的规格化值,而增大指数为有益于提高浮点数的表示范围得到更小更精确的规格化值,**我们可以试想下列情况:

        ​ 设阶码位为k, 尾数位为n,偏置量bias

        ​ 32位环境下的某小数0.001953的二进制表示形式为:

        0 ( s , 1 位 ) 0111010 1 ( e x p , 8 位 ) 1111111111110111100111 0 ( f r a , 32 位 ) 0_{(s,1位)}01110101_{(exp,8位)} 11111111111101111001110_{(fra,32位)} 0(s,1)01110101(exp,8)11111111111101111001110(fra,32)

        ​ 其计数表示为 1.1111111111110111100111 ∗ 2 − 10 1.1111111111110111100111 * 2^{-10} 1.1111111111110111100111210

        ​ 放到k = 4,n = 3,bias = 7的机器环境中表示为:

        0 ( s , 1 位 ) 000 0 ( e x p , 4 位 ) 00 1 ( f r a , 3 位 ) 0_{(s,1位)}0000_{(exp,4位)} 001_{(fra,3位)} 0(s,1)0000(exp,4)001(fra,3)

        ​ E = 1 - 7 = -6, M = 0.001 ,故0.001953在该环境下表示为 0.001 ∗ 2 − 6 0.001* 2^{-6} 0.00126

        ​ ( 0.00000000 1 B 0.000000001_{B} 0.000000001B,显然是1/512的近似结果,具体看CSAPP图2-35参考),

        ​ 若要让小数点到达1前,阶码值需要到-9,即规格化表示是 1.0 ∗ 2 − 9 1.0 * 2^{-9} 1.029,可是该环境下的阶码E范围为 -6 ~ 7,显然超出了取值范围,E取最小值也无法满足开头是1的规格化条件,只能表示成非规格形式。

        ​ 另外可见,如果表示的浮点数的计数表示超过了E的最大范围,就会导致溢出,值变为 + ∞ +\infty +。比如上述环境中256.0的计数表示为 1.0 ∗ 2 8 1.0* 2^8 1.028, 用4个指数位已经不够表示了(E = 14(1110) - 7 = 7 < 8(1111)),于是全1溢出。

      • 特殊值

        ​ 特殊值包括正无穷,负无穷和NaN(即不是一个数,Not a Number), 在阶码全为1的时候出现,其中当小数位全为0时,s=1表示负无穷,s=0表示正无穷,如果小数位不全为数就是NaN, 它们的用处是用于表示未初始化的数据,比较少见,这里不再详谈。

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ssuzmmJn-1641887708855)(C:/Users/hw/AppData/Roaming/Typora/typora-user-images/image-20211029235949624.png)]

      参考:

      http://kaito-kidd.com/2018/08/08/computer-system-float-point/

      南京大学 计算机系统基础(一)主讲:袁春风老师

  • 2.4.4 舍入

    浮点数有4种舍入方式:向上舍入(往数轴值高方向舍入),向下舍入(往数轴值低方向舍入)向0舍入,向偶数舍入。

    前三种不难理解,最广泛的使用是向偶数舍入:对浮点数x,如果该数不在两数的中间值时,按就近原则舍入;如果正好在中心值,则要保证舍入后的最低有效位是偶数。比如:

    ​ 对1 < 1.5 < 2, 2 < 2.5 < 3, 1.5和2.5会分别向上舍入,向下舍入为2。

    这种舍入特性的优点是,对于求一组数据的平均值时,如果采用向上舍入求平均值会导致测值偏高,如果采用向下舍入求平均值会导致测值偏低,向偶数舍入使得数的舍入有前50%的情况为向上舍入,50%的情况为向下舍入,测值误差最小。

  • 2.4.5 浮点运算

    浮点运算不具有结合性和分配性,而且由于其舍入的特性导致很多诡异的结果,比如(3.14+1e20)- 1e20 会等于 0.0 而不是3.14,计算时把3.14舍去了。

  • 2.4.6 C语言中的浮点数

    C语言中的浮点数采用向偶数舍入的方式。

    值类型从int转换到float,可能被舍入

    值类型从int或者float转换到double, 属于向上扩展,精度不变。

    值从double转到float, 如果值超出了float的范围会溢出。

    值类型从double和float转换到int,会被向0舍入,而且有可能溢出。例如当int x = 1e10(2147483637,即 T m a x T_{max} Tmax时,转换成float能保存精度,但转换回int时会出现不确定舍入值的现象,C语言规定出现此现象作溢出处理,最后打印的值会变为-2147483638。

扩展参考

https://www.atjiang.com/categories/#CSAPP3

C中int8_t、int16_t、int32_t、int64_t、uint8_t、size_t、ssize_t区别

[原码、反码、补码,计算机中负数的表示](

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值