在计算机中数据运算的本质其实各种门电路的组合,这些门实现了加减乘除运算,这么门的输入和输出都是由0和1组成。由此不难推出,在计算机的世界中是使用0和1存储和计算所有数据的。其实在内存和硬盘中也是如此。据说量子计算机可以维持多种状态,跳出0和1期望可以看到。
数据
数据在计算机中可以分为正整数,负整数,浮点数三种。
正整数
为了方便数据表示,计算机中定义多种数据类型,每种数据类型取值范围和占用的内存不同,我们可以根据他们占用的内存来计算出他们的取值范围。比如C语言中unsigned int类型(这里先说无符号类型),占用4个字节,一个字节由8个bit,所以一共有32个bit,很明显:32个比特全为1时值最大,全为0时值最小。所以他的最大值是+....+
=
,最小值则是0。
负整数
在计算机0和1的世界中,没有'-'号,意味着它没法直接表示一个负数,我们如果想表示一个负数,只能在程序中去体现:比如声明成有符号的数据类型signed int(一般简写为int),这个类型既可以表示负整数也可以表示正整数。这就意味着:在内存中有一段二进制数据,你可以把它当成正整数也可以负数,甚至浮点数。
好了,比如计算机中有这样一段二进制数据:1010,当作正整数我们可以理解成10,或者-10。这里有个问题,如果把1010理解成-10,那么10怎么表示?所以这里负数的表示跟正数略有不同。
为了更好的表示正数和负数,计算机规定:有符号数据的最高位是1则表示负数,为0则表示正数。而具体可以分为原码,反码,补码三种表示法(计算机就是这么令人惊讶)。
原码表示法:最高位为1表示负数,剩余数据的表示与正整数相同。比如01010表示正数10,11010表示负整数-10。这种表示方法最符合我们正常人的思维方式。
转换成数学表达式:,w最高位数,i表示第几个bit。
反码表示法:最高位为1表示负数,剩余的数据按照正整数的值按位取反,即:最高位1,然后对剩下的1010按位取反,得到0101,然后组合起来:10101。
转换成数学表达式:
看到这里有个问题,这两种表达方式式有点瑕疵:0的表示都有两种,原码表示法中10000(负)和00000(正)都表示0,反码表示法中,11111(负)和000000(正)都表示0(这里先假设数据只有5bit,如果是类型则是32个1),这样会浪费一个字节,而且也不利于计算。(注意无论是什么表示,正数的表达都是一样的),所以计算机出现了第三中表示方法。
补码表示法:最高位表示负数,剩余数据按照正整数的值按位取反,再加1,这同时也是反码转补码的方法。即:-10转成二进制是10110。
转换成数学表达式:
所以我们看到,补码表示法中11111表示-1,00000则表示0,这样负数就没办法表示0了,所以补码表示法中,所有能表示负数的个数要不正整数多一个,因为正整数中有一个00000被当做0了呀。比如int的取值范围:-2147483648 到 2147483647,现在计算机普遍使用补码表示法。
浮点数类似整数的表示法
浮点数即小数,延续负整数的思路,我们假设计算机中的小数用二进制表示的话可以让某一bit代表小数点,比如3.14可以表示成1101110,其中红色的零表示小数点,如果计算机采用这种方式的话,那么他的表示范围是多少呢?为了不浪费,我们规定前16bit是整数,后16bit是小数,以无符号int型为例,最大值是65535.65535。最大精度到小数点后五位。
定点表示法
然而实际中计算机采用的是另一种更为巧妙表示方法,同样的假设前16bit是整数部分,后16bit是小数部分,整数部分的计算方法不变,小数部分计算方法由改成
,即小数部分的11表示
,即0.75,以此类推,最小精度是
,这个精度显然要高的多了!
由此可见在计算机中其实小数使用分数来表示的,而且是可以除尽的分数,因为分母都是偶数。而且小数最后一位肯定都是5(因为分母都是偶数),那么问题来了3.14怎么表示?整数3不变,用11表示,0.14可以人肉计算一下,大概是1/8 + 1/64,即001001,转换成10进制就是0.140625,四舍五入,约等于3.14,要是把小数部分的16bit全用上,会出现更加接近的值。有里就有弊,有了足够高的精度,但是却牺牲了表示的准确性。
IEEE表示法
IEEE是IEEE为了统一表示标准而制定的。他用三个元素来表示浮点数:
符号(S):1表示负数,0表示正数,这一点和上面类似。
尾数(M):表示二进制小数。
阶码(E):表示浮点数的权重。
浮点数被IEEE定义为三种类型:
规格化的值:我们日常用到的小数值,阶码不能全为0或1
非规格化的值:正负0,阶码全为0
特殊值:正负无穷大,以及无理数,阶码全为1
看到这应该是一头雾水,以float类型规格化的值举例,他的符号位占1位,阶码占8位,尾数占23位,这次以-3.14为例:
符号位:很明显符号位是1
阶码:阶码实际是2的次幂,因为是规格化的数,所以8位取值是1-254,而阶码被规定可以是负数,所以IEEE就对他做了偏移运算,即11111110(十进制254)表示的是254-()=127,那么00000001(十进制1)表示的是1-(
)= -126,那么8位阶码的取值范围是(-126,127),很明显这里阶码只能是1,再大就会溢出,那么阶码的值就是1+127=128(二进制10000000),
到这里可以看到目前的值是这样的:,剩下就是M的值了。
尾数:我门直接人肉计算,M=3.14/2=1.57,转换二进制约等于1/2 + 1/16 + 1/32=0.5675,即110011,红1表示整数1,后面的表示小数,因为尾数默认是加1的,所以应该是1011.
那么完整的-3.14二进制就是1(符号位) 10000000(阶码) 10011000000000000000000(尾数)
参考:<深入理解计算机系统>