我们常常提到的int整型,float单精度浮点型,double双精度浮点型等等,这些基本数据类型都是以符合人类世界的逻辑而出现的。这些数据类型出现的目的,便于让人容易理解,是架通人类思维与计算机的桥梁。但事实上,计算机中并没有这些数据类型,计算机能理解的数据只有0和1,所有的数据(文本、图像、音频、视频等)在计算机里面都是以0和1存储和运算。因此,符合我们人类思维的数据都要通过一定的转换才能被正确的存储到计算机中。

一、进制的概念

进制也就是进位计数制,是指用一组特定的数字符号按照进位规则来表示数的计数方法。人们常用的进制是十进制,但使用十进制并非是天经地义的,它只不过是来源于远古时代用十指记数的一种约定俗成的习惯。 对于任何一种进制X进制,就表示某一位置上的数运算时是逢X进一位。 八进制是逢八进一,十进制是逢十进一,十六进制是逢十六进一,二进制就是逢二进一。
进位计数制的组成:基码、基数、位权。
基码:组成该数的符号集,包括数字和字母。
基数:基码的个数r就是进位计数制的基数,也称为r进制。例如组成十进制的基本符号有0、1、2、3、4、5、6、7、8、9共计10个,则十进制的基数是10。
位权:一个数字符号处在某个位上所代表的数值是其本身的数值乘上所处数位的一个固定常数,这个不同数位的常数称为位权(简单的说就是位数的次幂)。例如十进制888中,第1个8代表8*10^2,第2个8代表8*10^1,第3个8代表8*10^0。常数10^2、10^1、10^0分别是第1个8、第2个8、第3个8的位权。
例如:十进制1234.55可表示为 (1234.55)10=1×10^3+2×10^2+3×10^1+4×10^0+5×10^(-1)+5×10^(-2),可以看出,各种进位计数制中位权的值恰好是基数的某次幂。因此,对任何一种进位计数制表示的数都可以写成按位权展开的多项式。
注意: 当各种计数制同时出现的时候,我们可以用下标加以区别,也可以用其英文的缩写,将(2836.52)10表示为2836.52D,将(110.101)2、(16.24)8、(5E.7)16分表表示为110.101B、16.24O、5E.7H。

二、八进制和十六进制出现是为什么?

人类一般思维方式是以十进制来表示的,而计算机则是二进制,但是对于编程人员来说,都是需要直接与计算机打交道的,如果给我们一大串的二进制数。比如说一个4字节的int型的数据:0000 1010 1111 0101 1000 1111 11111 1111,我想任何程序员看到这样一大串的0、1都会很头疼。所以必须要有一种更加简洁灵活的方式来呈现这种数据。
因此为了更简洁的表达计算机的思维方式,出现了八进制、十六进制,其实十六进制应用的更加广泛,就比如说上面的int型的数据,直接转换为八进制的话,32/3 余2 也就是说,我们还要在前面加0,但是转换为十六进制就不同了。32/4=8,直接写成十六进制的8个数值拼接的字符串,简单明了。
所以说用十六进制表达二进制字符串无疑是最佳的方式,这就是八进制和十六进制出现的原因。

三、不同数制间的转换

0.png

3.1 非十进制数转换成十进制数(按权展开求和)

这里的“非十进制”指的是二进制、八进制与十六进制的一种。
例一:将(1011.101)2 转换成十进制数。

(1011.101)2  = 1*2^3+0*2^2+1*2^1+1*2^0+1*2^-1+0*2^-2+1*2^-3
             = 8+2+1+0.5+0.125
             = (11.625)10

例二:将(A3.2C)16转换成十进制数。

(A3.2C)16 = A*16^1+3*16^0+2*16^-1+C*16^-2
          = 10*16^1+3*16^0+2*16^-1+12*16^-2
          = 160+3+1/8+3/64
          = (163.172)10

例三:将(1657)8转换成十进制。

(1657)8 = 1*8^3+6*8^2+5*8^1+7*8^0
        = (943)10

3.2 十进制转换成非十进制

3.2.1 整数部分(除基取余,倒着排)

将十进制整数转换成其它整数采用“除基取余”, 将余数倒序排列, 直到商小于1。
例一:将十进制数94转换成十六进制数。
十进制数94转换成十六进制数是5E。
1.png
例二:将(25)10转换成二进制数。
2.png
例三:将(125)10转换成八进制数。
3.png

3.2.2 小数部分(乘基取整,正着排)

小数部分乘基数后将整数部分取出来顺序排列,直至小数部分为0则停止。最后前面加上0. 。
例一:将(0.125)10转换成二进制数。
4.png
(0.125)10=(0.001)2
例二:将(0.625)10转换成十六进制数。
6.png
(0.625)10=(0.A)16

3.3 二进制转换成八进制

规则:以小数点为中心,分别向左、向右每三位为一组,首尾组不足三位时,首尾用“0”补足,再将每组二进制数转换成一位八进制数码,此方法也被称为三位分组法。
例一:将(1010011.01011)2转换成八进制数。

(001010011.010110)2 =(123.26)8

3.4 八进制转换成二进制

规则:将每位八进制数用三位二进制数表示即可。
例一:将(617.34)8转换成二进制数。

(617.34)8=(110001111.011100)2

3.5 二进制转换成十六进制

规则:以小数点为中心,分别向左、向右每四位为一组,首尾组不足四位时,首尾用“0”补足,再将每组二进制数转换成一位十六进制数码,此方法也被称为四位分组法。
例一:将(1101111100111.1001111101)2转换成十六进制数。

(0001101111100111.100111110100)2=(1BE7.9F4)16

3.6 十六进制转换成二进制

规则:将每位十六进制数用四位二进制数表示即可。
例一:将(617.34)16转换成二进制数。

(617.34)16=(011000010111.00111000)2

四、数据在计算机中的存储

4.1 整型在内存中的存储

计算机中整数的使用可分为两种方式:一种只能表示非负数,称为无符号整型数;另一种能够表示负数、0以及正数,称为有符号整型数。

4.1.1 无符号整型数的存储

一个无符号整数可以用n个二进制位表示,所有二进制位都用来表示数值。无符号整数存储步骤如下:

  1. 将整数转化为二进制 ;
  2. 如果二进制的位数不足n位,就在二进制的左边补0。
    7.png

对于一个n位的二进制无符号数,在计算机中能容纳的范围为 0 ~ 2^n − 1。

4.1.2 有符号整型数的存储

计算机中有符号整数有三种表示方式:原码、反码、补码。在计算机系统中,有符号整数采用补码方式存储。

4.1.2.1 原码

将一个十进制的数直接转换为二进制序列,这个二进制序列就是他的原码。对于有符号整数,如果用n个二进制位表示一个有符号整数,约定最左边一位用作符号位,其余(n-1)位用于表示数值。正数的符号位为0,负数的符号位为1,数值位表示有符号整数的绝对值,所以原码又称带符号的绝对值。
8.png
一般地,如果用n个二进制位表示一个十进制有符号整数x,则x的取值范围是(-2^(n-1), 2^(n-1))或写为(-2^(n-1)<x<2^(n-1))。
例如:-16 ,16是一个整型,整型的大小是4个字节,4个字节是32个bite位。
10000000 00000000 00000000 00010000,这就是-16的原码,由32个0/1组成。

4.1.2.2 反码

正数反码跟原码是一样的;负数时,反码就是原码最高位(符号位)除外,其他位按位取反。
11111111 11111111 11111111 11101111,这就是-16的反码。

4.1.2.3 补码

正数的补码就是其本身,负数的补码是在反码的基础上+1。
负数的补码计算方法如下:

  1. 首先,将负数的绝对值转换为二进制形式。
  2. 然后,将二进制数的每一位取反。也就是将 0 变为 1,将 1 变为 0。
  3. 最后,将得到的结果再加 1。

4.2 浮点型在内存中的存储

浮点数在内存中也是以二进制的形式存储的,不过小数部分的二进制表示形式与整数部分的二进制表示形式不同,并且浮点型的数据在内存中的存储方式也没有整型那么简单,其中有一些复杂的规则。
IEEE 754标准规定一个浮点数可以统一写成 (-1)^S * M * 2^E 。 例如5.625转换为二进制后是101.101,并且101.101可以写成1.01101*2^2。

  • 符号位(Sign,S):表示浮点数是正数还是负数。当S=0时,表示这个浮点数为正值;S=1表示这个浮点数为负值。
  • 尾数位(Mantissa,M):表示基数部分,即数值部分,尾数位决定了浮点数的精度(有效数字),取值范围:1≤M<2。上例中有效数字M就是1.01101。
  • 阶码位(Exponent,E):表示指数部分,所以又称指数位,表示一个数的幂值,指数位决定了浮点数的取值范围和绝对值最小的非零数,其在内存中是以无符号形式存储的。2^E表示指数位,上例中E=2。

IEEE 754还统一对S、E、M在内存中的大小及存储的位置进行了规定。
对于float单精度浮点数,第1位为符号位S,接着8个位存储E指数位,最后23个位存储有效数字M。
9.png
对于double双精度浮点数,第一位为符号位S,接着11个位存储E指数位,最后52个位存储有效数字M。
10.png

4.2.1 尾数M

对于尾数M,1≤M<2 ,也就是说,M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。 IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的 xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位 浮点数为例,留给M只有23位, 将第一位的1舍去以后,等于可以保存24位有效数字。

4.2.2 指数E(阶码)

  • 将E存入内存:首先E为一个无符号整数,这意味着,如果E为8位,它的取值范围为0~255开区间;如果E为11位,它的取值范围为0~2047开区间。但是,我们知道,科学计数法中的E是可以出现负数的,所以IEEE 754规定,存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间 数是1023。比如,2^10的E是10,所以保存成32位浮点数时,必须保存成10+127=137,即 10001001。
  • 将浮点数取出内存:将E加上127/1023存入内存后,在使用时要将E、M再取出来,但E的情况不同取出的方法不同。在一般情况下将指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。比如:0.5(1/2)的二进制形式为0.1,由于规定正数部分必须为1,即将小数点右移1位,则为 1.0*2^(-1),其阶码为-1+127=126,表示为 01111110,而尾数1.0去掉整数部分为0,补齐0到23位00000000000000000000000,则其二进制表示形式为:0 01111110 00000000000000000000000。
  • E全为0:这时浮点数的指数E等于1-127(或者1-1023),有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
  • E全为1:这时如果有效数字M全为0,表示±无穷大(正负取决于符号位s)。

五、总结

本文主要是为了理清各种数制之间的关系,以及数据在内存中的存储形式。这有助于后续了解在一些运算中尤其是浮点数运算中出现的精度问题。