float、double 的二进制表示、移码、舍入方案

1.官方文档

  ieee 754-2019   :  https://ieeexplore.ieee.org/document/8766229

2.十进制小数 -> 二进制小数

2.1 规则

  整数部分用2进制,小数部分×2 取整得到小数第1位,剩余的小数部分继续×2取整得到小数第2位, ...   直到小数部分为0或者有效位数用尽(这时采用舍入方案,见 2.3 浮点数的舍入方案).

2.2 示例

  以 13.671 为例  ,它的二进制为  1101.101011 ,过程如下:

  • 将整数部分13 转为2进制      , 得到 1101
  • 0.671 × 2  =  1.342  取整为1 ,剩余0.342 ;小数第1位为1 
  • 0.342 × 2  =  0.684  取整为0 ,剩余0.684 ;小数第2位为0 
  • 0.684 × 2  =  1.386  取整为1 ,剩余0.386 ;小数第3位为1
  • 0.386 × 2  =  0.736  取整为0 ,剩余0.736; 小数第4位为0
  • 0.736 × 2  =  1.472  取整为1 ,剩余0.472; 小数第5位为1
  • 0.472 × 2  =  0.994  取整为0 ,剩余0.994; 小数第6位为1,而不是0, 因为发生了舍入,为啥是1 ? 参考 2.3 浮点数的舍入方案.

  特殊情况: 小数部分出现循环,无法停止,则用有限的二进制位无法准确表示一个小数,这也是在编程语言中表示小数会出现误差的原因.如: 

    0.6 * 2 = 1.21.2 取整为1余 0.2  —————— 1 
    0.2 * 2 = 0.40.4 取整为0余 0.4  —————— 0 
    0.4 * 2 = 0.80.8 取整为0余 0.8  —————— 0 
    0.8 * 2 = 1.61.6 取整为1余 0.6  —————— 1 

    0.6 * 2 = 1.21.2 取整为1余 0.2  —————— 1 
    0.2 * 2 = 0.40.4 取整为0余 0.4  —————— 0 
    0.4 * 2 = 0.80.8 取整为0余 0.8  —————— 0 
    0.8 * 2 = 1.61.6 取整为1余 0.6  —————— 1 
    …………

   0.6用二进制表示为 1001 1001 1001 1001 …… 

2.3 浮点数的舍入方案

  IEEE 浮点格式定义了 4 种不同的舍入方式,如下表所示。其中,多数CPU与浮点运算器默认采用 [ 向偶数舍入 ] 方案,而其他三种可用于计算上界和下界。

  

示例1:

  

示例2:

     1.235,向上是 1.24;向下/向零 1.23。误差相同都是0.005。这时优先采用向偶数舍入,即1.24。

          1.245,向上是 1.25;向下/向零 1.24。误差相同都是0.005。这时优先采用向偶数舍入,即1.24。

示例3:

     1.234999,保留两位小数时,就没有分歧。向上/向偶 1.24   误差为0.0050001;向下/向零 1.23  误差为0.0049999.向下的误差最小,所以系统会采用1.23。 

     1.235001,舍入到1.24的误差值要小于舍入到1.23的误差值,没有分歧. 

为什么要在分歧的时候采用向偶数舍入? 

  采用向上舍入一组数值,会在计算这些值得平均数中引入统计偏差。采用这些方式舍入得到的一组数的平均值将比这些数本身的平均值略高一些。

  相反,采用向下舍入一组数值,得到的平均值会比本身的平均值略低一些。

  向偶数舍入在大多数现实情况中避免了这种统计偏差。因为在50%的时间内,他将向上舍入;在50%时间内,他将向下舍入。

注意事项:

  由IEEE浮点格式定义的舍入方式可知,不论使用哪种舍入方案,都会产生舍入误差。如果在一系列运算中的一步或几步产生了舍入误差,在某些情况下,这个误差将会随着运算次数的增加而积累得很大,最终会得出没有意义的运算结果。因此,建议不要将浮点数用于精确计算。

3.二进制小数 -> 十进制小数

3.1 规则

  小数部分:第1位数 × 2 -1  第2位数 × 2-2  +  ...  +  第n位数 × 2-n

3.2 示例

  0.6(2)  =  1001 1001 1001 1001 ……

  0.6(10) = 1 × 2-1 + 0 × 2-2 + 0 × 2-3 + 1 × 2-4 + ……

3.3 二进制十进制转换复习

  10 -> 2  : 

    整数部分[ 除2取余,逆排],小数部分[乘2取整,顺排]

  2 -> 10  :

    整数部分 [ 从右向左, 第1位数 × 20   +  第2位数 × 21   +  ...  +  第n位数 × 2n-1  ]

    小数部分 [ 从左向右, 第1位数 × 2 -1  +  第2位数 × 2-2  +  ...  +  第n位数 × 2-n    ]

4.float 、double的二进制

  IEEE 754规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80位实现)。只有32位模式有强制要求,其他都是选择性的。在计算机中浮点数用2进制的科学技数法表示。

4.1 二进制的科学技数法

  具体为  :   1.xxx(尾数)*  2(指数) , n 是非0 整数 , 示例如下:

                2进制的科学计数法

4.2 二进制存储 

  IEEE 于 1987 年推出了与底数无关的二进制浮点运算标准 IEEE 854,并于同年被美国引用为 ANSI 标准。1989 年,国际标准组织 IEC 批准 IEEE 754/854 为国际标准 IEC 559:1989。后来经修订后,标准号改为 IEC 60559。现在,几乎所有的浮点处理器完全或基本支持 IEC 60559。同时,C99 的浮点运算也支持 IEC 60559。
  IEEE 浮点数标准是从逻辑上用三元组{S,E,M}来表示一个数 V 的,即 V =(-1)S × M × 2E,如下图所示。

  要存以科学计数法表示的浮点数,需要存储三个部分: 符号,尾数,指数,如下图所示:

 

  • 最高位有1bit存储正负号
  • 指数部分占据8bits(float 4字节)或11bits(double 8字节),取值范围是:float为 [ -126 ~ 127 ] ,double为 [ -1022 ~ 1023 ]。
  • 其余部分全都用来存储尾数。

4.3 为什么指数位要加偏移量?(移码)

  本节以float为例,double同理。

  指数可能为正数或者负数,当为负数时。如果这存储指数的这8位用 [ 补码方案 ] 的话,还要用一个符号位,这时在它前面还有一个数值本身的符号位,再加上一个指数的符号位,容易让上困惑,并且实现起来麻烦,专家们想到了一个更巧妙的方案就可以解决指数为负数的情况,就是 [ 移码方案 ] :

  • 原指数在存储时加上127 
  • 提取时减法127就可以得到真实的指数值
  • 为什么是127 ?

4.4 为什么float的偏移量为127?

  IEEE 标准指定了以下特殊值:±0反向规格化的数±∞ NaN(如下表所示)。这些特殊值都是使用 emax+1 或 emin-1 的指数进行编码的

 

NaN:当指数段 exp 全为 1 时,小数段为非零时,结果值就被称为“NaN”(Not any Number),如下图所示

  

±∞ 当指数段 exp 全为 1,小数段全为 0 时,得到的值表示无穷。当 s=0 时是 +∞,或者当 s=1 时是 -∞。如下图所示

  

非格式化值:当指数段 exp 全为 0 时,所表示的数就是非规格化形式,如下图所示。  

结论:

  指数范围本来应该为 [ 0000 0000 - 1111 1111 ] ,但是去掉特殊值和非格式化值后。最终指数的范围[0000 0001 - 1111 1110],10 进制的 [1 - 254],所以实际表示的指数值是(1-127 到 254-127)=(-126,127)

4.5 float0.6二进制示例

 

 float a = 0.6在内存中被存为了数字0x3F19999A,如果把4个字节看作是长度为4的byte数组,不同的计算机对这个数组有不同的存储方式。

  • 在大端模式下,这个数组为[0x3F, 0x19, 0x99, 0x9A],即数字的高位在数组靠前位置;
  • 在小端模式下,这个数组为[0x9A, 0x99, 0x19, 0x3F],即数字的高位在数组靠后位置;

4.6 double -0.1 二进制示例

 

posted @ 2015-04-28 18:27  f9q  阅读(1248)  评论(0编辑  收藏  举报