• 关于float型数据在内存中的存储方式的讨论


    从一道面试题说起:

    我根据自己的理解已经网上的相关资料,对程序作了一点点改变及注释,程序如下:

     1 #include <iostream>
     2 #include <stdio.h>
     3 #include <string.h>
     4 #include <conio.h>
     5 #include <stdlib.h>//与#include<cstdlib>效果一样:包含 system()函数
     6 using namespace std;
     7 
     8 int main()
     9 {
    10  float a = 1.0f;
    11 
    12  cout << sizeof(int) <<endl;//4
    13  cout << sizeof(float) <<endl;//4
    14 
    15  cout << (int)a << endl;//1
    16  cout << &a << endl; /*取a的地址十六进制0012FF7C*/
    17  cout << (int)&a << endl;/*(int)&a:把a的地址强制转换成十进制的整型1245052*/
    18  cout << (int&)a << endl;
    19  /*(int&)a:将a的引用强制转换为整型,意思是a所在的内存,本来定义的时候为float类型,并初始为1.0f,
    20  但现在我要按int类型解释这段内存(也就是说a所在的内存地址中的数据本来是按float型存储表示的,你非要按int型来解释不可)。
    21  1.0f   在内存中的存储为
    22  0   011   1111   1   000   0000   0000   0000   0000   0000.
    23  把他按整型数解释为2^29+2^28+2^27+2^26+2^25+2^24+2^23=1065353216
    24 
    25  (int&)a 相当于
    26     *(int*)&a
    27 
    28     *(int*)(&a)
    29 
    30     *((int*)&a)
    31 
    32  */
    33 
    34  cout << boolalpha << ((int)a == (int&)a ) << endl;//   输出false.因为1!=1065353216.
    35 
    36  float b = 0.0f;
    37  cout << (int)b << endl;//0
    38  cout << &b << endl;/*取b的地址十六进制0012FF78*/
    39  cout << (int&)b << endl;//0
    40  cout << boolalpha << ((int)b == (int&)b ) << endl;//   输出true,因为0==0;
    41 
    42 /*
    43   (int&)a   不经过转换,   直接得到a在内存单元(就是地址)的值
    44   (int)a     a在内存中的值转换成int类型
    45 
    46   float类型在内存中存储的形式是   ,符号位   指数   尾数
    47   由754标准:阶码采用增码(该数补码的反符号),尾数采用原码
    48   所以1.0f   在内存中的形式为
    49   0011   1111   1000   0000   0000   0000   0000   0000
    50   所以输出的是   0x3f800000
    51   0   在内存中的的存储形式
    52   0000   0000   0000   0000   0000   0000   0000   0000
    53 */
    54 system("pause");
    55  return 0;
    56 }

    第一点:#include<cstdlib>和#include<stdlib.h>效果是一样的,主要是为了兼容c函数库。system()包含在这个头文件下,system(pause)的作用是在直接执行EXE终端时,不会使终端一闪而过。直到按回车键,才退出程序。

    第二点:就如上面的注释讲的一样(int&)a:将a的引用强制转换为整型,意思是a所在的内存,本来定义的时候为float类型,并初始为1.0f20 但现在我要按int类型解释这段内存(也就是说a所在的内存地址中的数据本来是按float型存储表示的,你非要按int型来解释不可)。至于这个float在内存中怎么表示,如下叙述先说说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 , 二进制就是 10000010 套用上面话就表示为:
         0  10000010 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 之类就默认是相等.  所以这就出现了经典的 精度问题.

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

    所以 double型的范围是  -1.79769 * e308 ~ +1.79769 * e308
    多用了几位, 表示范围大了很多, 其实本质跟float型一样.
    参考网页:
    http://blog.sina.com.cn/s/blog_3d10a88301000aok.html    
    http://blog.csdn.net/liaozhen/article/details/1778165



     

      

  • 相关阅读:
    javascript 备忘 细节 相关
    内存泄漏
    css相关 细节 优化 备忘
    nodeType
    事件冒泡 模型
    两个感叹号作用
    非冒泡事件
    DOM 对象
    Quartz2D 之 绘制文本
    Voilin 与 乐谱
  • 原文地址:https://www.cnblogs.com/caiyineng/p/5048690.html
Copyright © 2020-2023  润新知