#浮点数在计算机中的表示方法 [convert_float1]: http://data.linuxtoy.cn/image/convert_float.png [convert_float2]: http://data.linuxtoy.cn/image/convert_float2.png [IEEE754_float]: http://data.linuxtoy.cn/image/IEEE754_float.png [最大规约数计算过程]: http://data.linuxtoy.cn/image/tangtang_float.jpg [mod_by_2]: http://data.linuxtoy.cn/image/mod_by_2.png [multiply_by_2]: http://data.linuxtoy.cn/image/multiply_by_2.png __reference__: [1. CSDN 浮点数在计算机中的表示](http://blog.csdn.net/misterliwei/article/details/5455666) [2. IEEE_754 ](http://zh.wikipedia.org/zh-cn/IEEE_754) # 转换 * 一个十进制的浮点数,例如:abcd.efg (其中a~g为0..9),用多项式表示为: `a*10^3 + b*10^2 + c*10^1 + d*10^0 + e*10^(-1) + f*10^(-1) + g*10^(-3)` * 一个二进制的浮点数,我们也将其表示成:abcd.efg (其中a~g为0或1),用多项式表示为: `a*2^3 + b*2^2 + c*2^1 + d*2^0 + e*2^(-1) + f*2^(-1) + g*2^(-3)` ## 转换方式1 例如: 将0.842356 转换为二进制的表示方式 依次与2^(-n)次方比较(n从1开始), 若大于该值则为1,且减去此值,否则为0, 然后进行下一轮比较 ![将0.842356转换为二进制][convert_float1] 由上图得知:比较是无穷无尽的,十进制小数转换成二进制时只是一个近似值 如果要截取到某位,需要做一些取舍,取舍的标准: `其后一位若为1则进1,后1位为0则不进` 以上图为例: 1. 若要截取到第9位,由于第10位为0,故不进位, 结果为: 0.110101111 2. 若要截取到第8位,由于第9位为1,故要进位,结果为: 0.11011000 ## 转换方式2 转换方式1的计算过程存在的问题: 2^(-n)本身就是一个浮点数,而浮点数之间的比较和计算难免有误差,不适合计算机运算。所以采用下面这个方法: 1. 生成首数字为1、后面0的个数为小数位数的基准数,比如0.254的基准数为1000、0.00353的基准数为100000。 2. 将小数部分乘以上面的基准数,这样得到一个整数。 3. 对该整数乘以2,若积大于基准数,则为1,同时将积减去基准数后得新的整数;若积小于基准数,则为0。 4. 用新的整数重复步骤3,直到整数为0或者到需要的精确位数,作取舍后结束。 例如:将0.842356转换成二进制,基准数为1000000,转换成整数为842356, ![将0.842356转换为二进制方式二][convert_float2] **手动计算可以采用如下方法** 乘2取整的方法, 相当于每次左移一位,如果大于1, 则相应的位为1 > 0.842356 X 2 = 1.684712, 取整数位1, 得二进制小数 0.1 0.684712 X 2 = 1.369424, 取整数位1, 得二进制小数 0.11 0.369424 X 2 = 0.738848, 取整数位0, 得二进制小数 0.110 0.738848 X 2 = 1.477696, 取整数位1, 得二进制小数 0.1101 0.477696 X 2 = 0.955392, 取整数位0, 得二进制小数 0.11010 0.955392 X 2 = 1.910784, 取整数位1, 得二进制小数 0.110101 0.910784 X 2 = 1.821568, 取整数位1, 得二进制小数 0.1101011 0.821568 X 2 = 1.643136, 取整数位1, 得二进制小数 0.11010111 0.643136 X 2 = 1.286272, 取整数位1, 得二进制小数 0.110101111 0.286272 X 2 = 0.572544, 取整数位0, 得二进制小数 0.1101011110 ....... > ## 十进制转换为非十进制的方法 整数部分: 除R取余法 小数部分:乘R取整法 R为进制的基数 如下图: ![除2取余][mod_by_2] ![乘2取整][multiply_by_2] # 能精确表示的浮点数 *0.1在计算机中可以精确地表示吗?* 在计算机中大多数的浮点数都不能精确地表示 将0.1按照乘2取整的方式表示: * (1) 0.1 x 2 = 0.2 取整数位 0 得 0.0 * (2) 0.2 x 2 = 0.4 取整数位 0 得 0.00 * (3) 0.4 x 2 = 0.8 取整数位 0 得 0.000 * (4) 0.8 x 2 = 1.6 取整数位 1 得 0.0001 * (5) 0.6 x 2 = 0.2 取整数位 1 得 0.00011 * (6) 0.2 x 2 = 0.4 取整数位 0 得 0.000110 * (7) 0.4 x 2 = 0.8 取整数位 0 得 0.0001100 * (8) 0.8 x 2 = 1.6 取整数位 1 得 0.00011001 * (9) 0.6 x 2 = 1.2 取整数位 1 得 0.000110011 ... ... 注意到乘以2之后的结果出现了循环, 也就是说0.1的二进制是一个无限循环小数: 0.000110011 ... ... 从0.1 到0.9中只有0.5可以被精确表示, 因为0.5是 2的-1次方 # 编码方式 IEEE754 规定的浮点数格式: 单精度, 双精度, 扩展精度 浮点数分为 3部分 进行存储: 符号位(Sign) : 0 表示正数, 1表示负数 指数位(Exponent): 用于存储科学计数法中的指数数据, 并采用移位存储 尾数位(Mantissa): 尾数部分 指数偏移值(exponent bias),指浮点数表示法中的指数域的编码值为指数的实际值加上某个固定的值, IEEE 754标准规定该固定值为2^(k-1) - 1 IEEE 标准从逻辑上用三元组{S,E,M}表示一个数 N,如下图所示: ![IEEE754_float][IEEE754_float] n s e m 分别为 N S E M对应的实际数值 * 单精度浮点数: N 共32位, 其中S占1位, E占8位, M占23位 * 双精度浮点数: N 共64位, 其中S占1位, E占11位, M占52位 M 虽然是23位或者52位, 但他们只是表示小数点后面的二进制位数. 标准规定小数点 左边还有一个隐含位, 这个隐含位有可能是1 也有可能是 0 假如 M = 010110011... 最后m的结果可能是 m=1.010110011... 或者m=0.010110011... 这个隐含位到底取1还是取0呢? 只有在n非常小的时候,隐含位才取0, 这里需要引入两个概念: 规格化(normallized) 和 非规格化(denormalized) ## 规格化与非规格化 是否为规格化全看 E ### 规格化 当E的二进制位不全为0 并且 不全为1 时, N为规格化形式. 此时e被解释为表示偏置(biased)形式的整数 采用这种形式表示e的目的是为了能够区分正指数和负指数 e 的计算方式为: `e = |E| - bias` `bias = 2^(k-1) - 1` k 表示 指数位的位数, 对单精度来说k=8, bias=127, 对于双精度来说 k=11, bias=1023 此时m的计算方式为: `m = |1.M|` 即标准规定的 隐含位为1 ### 非规格化 当E的二进制位全为0 的时候, N为非规格化形式 此时e和m的计算方式为: `e = 1 - bias` `m = |0.M|` 小数点左侧的隐含位为0 主要是为规格化数值、非规格化数值之间的平滑过渡设计, 所以 e = (1-bias)而不是(-bias), === 特殊数值 === 当E的二进制位全为1时为特殊数值。 若M的二进制位全为0,则n表示无穷大. S为1则表示负无穷大,若S为0则表示正无穷大; 若M的二进制位不全为0, 表示NaN(Noy a Number), 表示这不是一个合法的实数或无穷, 或者该数未经初始化 === 单精度浮点数的极值 === | 类别 | 符号位 | 实际指数 | 指数域 | 尾数域 | 数值 | |--------------------|--------|----------|-----------|------------------------------|------------------------------------------------------| | 正零 | 0 | -127 | 0000 0000 | 000 0000 0000 0000 0000 0000 | 0.0 | | 负零 | 1 | -127 | 0000 0000 | 000 0000 0000 0000 0000 0000 | -0.0 | | 1 | 0 | 0 | 0111 1111 | 000 0000 0000 0000 0000 0000 | 1.0 | | -1 | 1 | 0 | 0111 1111 | 000 0000 0000 0000 0000 0000 | -1.0 | | 最小的非规约数 | * | -126 | 0000 0000 | 000 0000 0000 0000 0000 0001 | `± 2^(-23) * 2^(-126) = ± 2^(-149) ≈ ± 1.4*10^(-45)` | | 中间大小的非规约数 | * | -126 | 0000 0000 | 100 0000 0000 0000 0000 0000 | `± 2^(-1) * 2^(-126) = ± 2^(-127) ≈ ± 5.88*10^(-39)` | | 最大的非规约数 | * | -126 | 0000 0000 | 111 1111 1111 1111 1111 1111 | `± (1 - 2^(-23)) * 2^(-126) ≈ ± 1.18*10^(-38)` | | 最小的规约数 | * | -126 | 0000 0001 | 000 0000 0000 0000 0000 0000 | `± 2^(-126) ≈ ± 1.18*10^(-38)` | | 最大的规约数 | * | 127 | 1111 1110 | 111 1111 1111 1111 1111 1111 | `± 2^127 * (2 - 2^(-23)) ≈ ± 3.4*10^38` | | 正无穷 | 0 | 128 | 1111 1111 | 000 0000 0000 0000 0000 0000 | +∞ | | 负无穷 | 1 | 128 | 1111 1111 | 000 0000 0000 0000 0000 0000 | -∞ | | NAN | * | 128 | 1111 1111 | 不全为0 | NAN | 其中最大的规约数为: `± 2^127 * (2 - 2^(-23))` 是如何得到 ± 3.4*10^38 的? 计算过程如下图:(推导过程由唐海东同学提供) ![最大规约数计算过程][] # 浮点数精度的问题 精度由尾数决定 单精度浮点数尾数由23位组成,23Bit能表示的最大的十进制数为: `2^23 – 1 = 8388607` 也就是尾数部分最多可以精确到6位 由于还有一位隐含位,但是这一位可能为0,也可能为1,所以单精度浮点数的精度为6 ~ 7 位有效数字.