追逐理想
如果你了解我,你就会喜欢我
1. 浮点数的表示 与 类型转换
http://blog.chinaunix.net/u2/72955/showart_2056507.html
      
float a = 1.0f;
      cout << (int)a << endl;
      cout << (int&)a << endl;
      cout << boolalpha << ( (int)a == (int&)a ) << endl; // 输出什么?
    float b = 0.0f;
    cout << (int)b << endl;
    cout << (int&)b << endl;
    cout << boolalpha << ( (int)b == (int&)b ) << endl; //
输出什么?

这个题目涉及float在计算机中的存储问题, IEEE 754的标准就是描述的这个问题。如果这个题目放在笔试的时候应该比面试的时候容易多了。

这个题目的几个答案为110653532160x3f800000H),false00true。如果你已完美的答出这六个答案,就可以忽略后面的内容。

1.我们先来看一下IEEE 754中关于floatdouble的存储规范。

无论是单精度还是双精度在存储中都分为三个部分:

1. 符号位(Sign) : 0代表正,1代表为负

2. 指数位(Exponent:用于存储科学计数法中的指数数据,并且采用移位存储

3. 尾数部分(Mantissa):尾数部分

其中float的存储方式如下图所示:

 

指数部分(E) 占用8-bit的二进制数,可表示数值范围为0255。 但是指数应可正可负,所以IEEE规定,此处算出的次方须减去127才是真正的指数。所以float的指数可从 -126128.

尾数部分(M)实际是占用24-bit的一个值,由于其最高位始终为 1 ,所以最高位省去不存储,在存储中只有23-bit

符号位:s 通过(-1)的s次幂来表示正负号。

而双精度的存储方式为:

 

我们把EM从二进制串表示转换为真正的em
这里要涉及到规格化(normalized)”非规格化(denormalized)”规格化与否全看指数E下面分三种情况讨论E,并分别计算em:
1、规格化:当E的二进制位不全为0,也不全为1时,N为规格化形式。此时e被解释为表示偏置(biased)形式的整数,e值计算公式如下图所示:

 

 

上图中,|E|表示E的二进制序列表示的整数值,例如E"10000100",|E|=132,e=132-127=5 k则表示E的位数,对单精度来说,k=8,bias=127,对双精度来说,k=11,bias=1023

此时m的计算公式如下图所示:

 

标准规定此时小数点左侧的隐含位为1,那么m=|1.M|。如M="101",则|1.M|=|1.101|=1.625, m=1.625

2、非规格化:当E的二进制位全部为0时,N为非规格化形式。此时em的计算都非常简单。


注意,此时小数点左侧的隐含位为0   为什么e会等于(1-bias)而不是(-bias),这主要是为规格化数值、非规格化数值之间的平滑过渡设计的。后文我们还会继续讨论。有了非规格化形式,我们就可以表示0了。把符号位S1,其余所有位均置0后,我们得到了 -0.0; 同理,把所有位均置0,则得到 +0.0。非规格化数还有其他用途,比如表示非常接近0的小数,而且这些小数均匀地接近0,称为逐渐下溢(gradually underflow)”属性。
3、特殊数值:当E的二进制位全为1时为特殊数值。此时,若M的二进制位全为0,则n表示无穷大,若S1则为负无穷大,若S0则为正无穷大; M的二进制位不全为0时,表示NaN(Not a Number),表示这不是一个合法实数或无穷,或者该数未经初始化。

2.问题的解答

对于1.0f这个数字,我们应该如何表示?按照上面的规则可以得到,符号位为0,指数位为1270x7F),尾数部分M应该为全0。因此它在计算机中的存储就是0x3F800000H

cout << (int)a << endl;

a从浮点数转换为整数,由于1.0f能够使用32bits完整的表示,没有舍入误差,因此会输出整数 1
cout << (int&)a << endl;

a里面的内容转换为整数地址,因此编译器会直接浮点数的32位表示直接输出,,即0x3f800000h10进制表示。

对于0.0f这个特殊的表示上面已经提到了,它在内存中的存储就是全0,因此直接把浮点数转换为整数还是直接把浮点数的表示转化为整数地址结果都是0

正如上面提到的一样,浮点数-0的表示和+0的表示是不同的,-0在内存中的表示为0x80000000H

最后再转载一点关于long double的知识,我也没有自己深入。

3.扩展双精度格式(long double)

扩展双精度格式(SPARC 结构计算机)

4倍精度浮点环境符合IEEE关于扩展双精度格式的定义。该浮点环境的4倍精度浮点格式共128位,占4个连续32位字,包含3个构成字段:112位的小数f15位的偏置指数e,和1位的符号s。将这4个连续的32位字整体作为一个128位的字,进行重新编号。其中0110位包含小数f112126位包含偏置指数e;第127位包含符号位s。如图3所示

SPARC结构计算机中,地址最高的32位字存放小数的32位最低有效位,即f[31:0];但是在PowerPC结构计算机中,却是地址最低的32位字存放这些位

紧邻的两个32位字(在SPARC机中向下计算,在PowerPC机中向上计算)分别存放f[63:32]f[95:64]

最后一个字的第015位存放小数的最高16,f[111:96]。其中第0位存放该16位的最低有效位,第15位存放整个小数f的最高有效位。第1630位存放15位的偏置指数e,其中第16位存放偏置指数的最低有效位,第30位存放它的最高有效位。最高位,第31位存放符号s

 

扩展双精度格式(Intel x86结构计算机)

该浮点环境双精度扩展格式符合IEEE双精度扩展格式的定义。该浮点环境的扩展双精度格式共80位,占3个连续32位字,包含四个构成字段:63位的小数f1位显式前导有效位(explicit leading significand bitj15位偏置指数e,和1位符号位s。将这3个连续的32位字整体作为一个96位的字,进行重新编号。其中063包含63位的小数f,第63位包含前导有效位j6478位包含15位的偏置指数e,最高位第79位包含符号位s

Intel结构系计算机中,这些字段依次存放在十个连续的字节中。但是,由于 UNIX  System V Application Binary Interface Intel 386 Processor Supplement (Intel ABI) 要求双精度扩展参数,从而占用堆栈中3个相连地址的32位字,其中最高一个字的高16位未被使用

 

地址最低的32位字存放小数f的低32位,即f[31:0]。其中第0位存放整个小数f的最低有效位LSB 31位存放小数低32位的最高有效位MSB

地址居中的32位字,第030位存放小数f31位最高位,即f[62:32]。其中第0位存放31位最高小数位的最低有效位LSB,第30位存放整个小数的最高有效位,地址居中的32位字的最高位第31位存放显式的前导有效位j
    地址最高32位字里,第014位存放15位的偏置指数e,第0位存放偏置指数的最低有效位LSB,第14位存放最高有效位MSB,第15位存放符号位s。虽然地址最高的32位字的高16位在Intel x86结构系列机种未被使用,但他们对符合Intel ABI的规定来说,是必需的
=====================================================================================
2. 浮点型存储
http://blog.csdn.net/zjumath/archive/2009/05/12/4174248.aspx 

先说说32 位的 float型.
一个浮点数 X, 在计算机中表示为:
         X = a  *  2e  
  这里 e 代表指数,  a 代表尾数,   在 计算机内部, 他们都是用二进制表示的. 其中 a 用二进制的科学表示法表示, 由于科学表示法第一位总是1 (0除外) , 所以第一位略去不计.  e 表示的时候, 因为要表示出负数, 所以 要加上127 ,  实际运算的时候要减去 127.

IEEE 规定, 32 位 float型被拆开成以下格式, 左边为高位 :
           0                                 0000 0000                     0000000 00000000 00000000
        最高位,第32位            第 31-23位,共8位                第23-1位            
        符号位                            指数位                                   尾数位
      0为正,1为负               -127~+127                        0~0x 7f ff ff

float 的范围是 -3.40282 * e38 ~ + 3.40282 * e38

一般在人看来是 十进制的数, 要转换成二进制. 十进制转二进制, 大于1 的部分就是除以2 取余, 小于1 的部分乘2 取整(上计算机原理课,讲了那么多东西就记得这句话了)
 比如 8.5  转换成二进制就是 1000.1 , 处理成这一步, 还要用科学表示法表示, 就成了 1.0001 * 23 ,  注意: 由于1.0001 第一个1 要去掉, 所以成了 0001 , 3 需要加上 127 就成了 130 (移码运算,原码加上2^n-1), 二进制就是 10000011 套用上面话就表示为:
     0  10000011 0001000  00000000 00000000
     16 进制 就是:   0x 41 98 00 00 , 一般来说 , intel 系列的 CPU 都使用的是 小尾存放, 就是 高字节放在后面, 刚好要掉过来就是:   0x 00 00 98 41 , 这样就完成了一次浮点数的表示.

注意: 浮点数 0.0  在计算机中表示为 0x 00 00 00 00 .

那么浮点数的精度是怎么回事情呢? 当我们使用二进制表示 大于1 的部分的时候, 没有问题, 除以2,一直下去, 最后一位肯定不是1 就是 0; 那么小数部分呢?  举个例子, 比如 0.8
表示    0.8
             * 2
            1.6 - 1 = 0.6          
                             * 2
                             1.2 -1 = 0.2  - 0
                                            .* 2
                                             0.4 - 0
                                              *2     
                                             0.8 - 0
这样就循环了 就是说 0.8 的二进制 就是  0.11000 11000 ......  一直循环下去, 而我们计算机如果表示0.8只能取0后面的前25位(第一个1 略去, ^_^), 这就说明 如果是  0.80000000000000000000000001 , 它表示出来的值其实是和 0.8 一样, 所以我们比较float型的数字 用 a == b 其实是没有根据的, 一般都是 用 abs(a - b) < 0.000001 (0.000001应该是hex吧?此处涉及到数学的实数常识,公式|a-b|<c  转换成  -c<a-b<c)之类就默认是相等.  所以这就出现了经典的 \精度问题.

那么 double型呢? 咱们可以照 float 型的葫芦 来画了.
double 型 只是说 取 64 位, 比float型的位 多一倍, 但是同样 逃不出精度的五指山.  :)
IEEE 规定 double 型 ,
    第64位             63-54                                                53-1
   符号位            指数位 ( -1024 - 1024)                    尾数位

所以 double型的范围是  -1.79769 * e308 ~ +1.79769 * e308
多用了几位, 表示范围大了很多, 其实本质跟float型一样.

看来计算机是用来计算的, 一点都不假, 所谓机器智能, 也是人加上去的,  这就更不用怀疑了.


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zjumath/archive/2009/05/12/4174248.aspx





posted on 2009-09-24 12:06  人间奇迹  阅读(2705)  评论(0编辑  收藏  举报