美文网首页计算机基础
C/C++中的整数和浮点数在计算机中是如何表示的?

C/C++中的整数和浮点数在计算机中是如何表示的?

作者: 1301c72 | 来源:发表于2017-09-07 10:30 被阅读0次

    一、整数在计算机中的表示

    在C/C++中,整数一般分为无符号数(unsigned char、unsigned short、unsigned int等)和有符号数(char、short、int、long等),在计算机中通过补码来表示,那么有童鞋会问了,不是有那什么原码反码之类的吗?为什么不用它们而偏偏用补码呢?

    一开始我也有这样的困惑,于是通过各种查,各种看,算是理解了一点点,这里不打算详细解释原码和反码,只举几个例子说明在计算机中为什么不用它们表示十进制数,而用补码来表示。

    我们能认识0、1、2、3、、、9等自然数,然而计算机却不认识,计算机“笨”的只认识0和1,所以那些大神们就通过各种手段,将现实世界中的各种信息都转化为一连串由0和1组成的序列。然后计算机就通过不同的解读方式,对这一系列的01串串根据具体的上下文解释成不同的信息。

    我们都知道1 + (-1) = 0,那么如果用原码、反码来表示这个会怎样呢?

    原码:

         原码         十进制
      0000 0001         1
    + 1000 0001        -1
    ----------------
      1000 0010        -2
    

    反码:

         反码         十进制
      0000 0001         1
    + 1111 1110        -1
    ----------------
      1111 1111        -0  // 需要通过转换为原码,才能直观的看出所表示的实际数字,原码:1000 0000
    

    通过上面两个例子,我们看到,不管是用原码还是反码,都不能正确的表达结果。另外,我们可以发现,在原码和反码中,对于0的表示有两种,一种是0000 0000(+0),一种是1000 0000(-0),这也是一种缺陷,最好的方式是在给定一个十进制整数的范围内,该范围内的每一个数在相应的二进制表示中有且只有一个与之对应,也就是存在一一映射的关系。那么如何解决上述的问题呢?

    终于等到补码登场了,一般主角都压轴,哈哈。补码,就是在反码的基础上再加1,比如-1的补码就是1111 1110 + 1 = 1111 1111,用补码来进行上面的计算就是:

         反码         十进制
      0000 0001         1
    + 1111 1111        -1
    ----------------
     1 0000 0000        0    // 最高位的1被丢弃
    

    结果为0,符合我们的实际逻辑。我们再来看看前面提到的一一映射的问题,在C/C++中,char表示一个8bit的有符号整数,在计算机中用补码表示,所能表示的十进制范围为[-128, 127]共256个数,那么这个范围是怎么得来的呢?

       补码    十进制
    0000 0000    0
    0000 0001    1
    ...
    ...
    ...
    0111 1111    127(2^7 - 1)
    1000 0000    -128(-2^7 + 0)
    1000 0001    -127(-2^7 + 1)
    ...
    ...
    ...
    1111 1111    -1(-2^7 + 127)
    

    从上面可以看出,前128个数表示正数(最高位以0开始),后128个表示负数(最高位以1开始)。对于unsigned char来说,由于其为无符号数,所以所能表示的最大范围为[0, 255]。对于其它的整数数据类型可以通过类似的方法得到他们的表示范围,感兴趣的童鞋可以自己算算试试。

    二、小数在计算机中的表示

    对于float来说,在一般机器上都是以四个字节即32bit来表示,那么它在计算机中是怎么表示的呢?

    IEEE 754规定,对于32位的浮点数,最高位的那位表示符号位s,紧接着的8位是指数E,剩下的23位为有效数字M或者称为尾数。

    图1 32位浮点数在计算机中的表示 图2 个人理解

    在十进制中,对于任何一个小数都可以用科学计数法来表示,比如123.567的科学计数法就是1.23456*10^20.0097就是9.7*10^-3;同样地,在二进制的世界中,任何一个小数也可以用类似的科学技术法来表示,只不过不在以10为底,而是以2为底。那么对于十进制的123.5670.0097在二进制中如何表示呢?

    十进制小数:123.567
    
    在二进制中的表示:
    1. 首先取整数部分123,用二进制中表示为1111011。
    2. 小数部分.567如何表示?
    我们都知道,在十进制整数进行二进制转化的时候有一个方法,就是对十进制数进行不断的除以2,然后取余数(要么为0,要么为1),然后按逆序排列就得到了十进制整数的二进制表示。
    那么对于十进制的小数该如何用二进制表示是否找到了灵感?对,就是对小数部分不断的乘以2,然后对得到的新小数取整数部分(要么为0,要么为1)直到小数部分为0或者已经达到精度上限(比如所取得0、1已经达到23位了,再继续乘下去,对于32位的float来说,已经不能继续存储了)。
    
    0.567 * 2 = 1.134 …… 取1 , 基数 = 0.134
    0.134 * 2 = 0.268 …… 取0 , 基数 = 0.268
    0.268 * 2 = 0.563 …… 取0 , 基数 = 0.563
    0.563 * 2 = 1.126 …… 取1 , 基数 = 0.126
    ……
    ……
    ……
    最终结果为:10010001001001101110100。(23位)
    所以.567在二进制中的表示为:.10010001001001101110100。
    因而123.567在二进制中的表示为:1111011.10010001001001101110100。
    用科学计数法表示为:1.1110 1110 0100 0100 1001 1011 1010 0 * 2^6。
    通过IEEE 754的规定,我们可以很容易将其在计算机中表示:
    符号位:0
    指数位:Exp - 127 = 6 --> Exp = 133 -->1000 0101
    尾数部分:1110 1110 0100 0100 1001 101
    【IEEE 754规定,在计算机内部保存M时(1.XXXXXXX),默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。】
    所以123.567在计算机中的完整表示就是:
    0 1000 0101 1110 1110 0100 0100 1001 101
    
    

    通过在线工具,可以验证上面结果的正确性,如下图:

    图3 123.567在计算机中的表示

    对于0.0097也可以用同样的方法得到其在计算机内部的表示,这里就不在赘述了,有兴趣的童鞋可以自己尝试一下,然后通过上面提到的在线工具验证一下。

    这里稍微再提一下,在C/C++中,float所能保持的精度一般为小数位6-7位,那么这个6或7是怎么得来的呢?通过上面我们知道,对于32位的浮点数,后23位表示的是小数部分,总共可以表示2^23 = 8388608,也就是说,后面的23位最多可以表示十进制小数的前7位小数位,通过四舍五入,至少可以保证6位的精度是正确的,至于第7位......呵呵!

    好了,通过上面的描述,基本了解了float在计算机内存是如何表示的了,那么double在计算机中如何表示呢?其实和float的表示原理是一样的,只是double一般用64位bit来表示,最高位还是符号位,但是指数部分用了11位来表示,而剩余的52位全用来表示尾数(所能表示的精度一般为2^52 = ?15-16位),其余的规则和32位的float没啥区别,所以这里就仅放一张图,各位童鞋可以慢慢去体会。

    图4 64位浮点数在计算机中的表示

    后记

    这是我在简书上发表的第一篇文章,希望自己能一直坚持下去,养成一个习惯。
    对于文章中的不足之处,恳请各位读者批评指正。
    简书给我的感觉就是三个字 -- 简洁、美。
    感谢这个平台。

    参考

    (完)

    相关文章

      网友评论

        本文标题:C/C++中的整数和浮点数在计算机中是如何表示的?

        本文链接:https://www.haomeiwen.com/subject/ufhbjxtx.html