1 符号位
计算机如何存储数据,因为计算机世界里面所有的数据归根结底都是由0和1来存储的,那么如何表达数值的正负呢?只知道书本上说是有一个符号位,当该符号位为0时,表示的是正数,为1时表示负数。我那时没搞懂为什么这样规定,我觉得1么,代表正数挺合理的,那么0就自然表示负数咯,所以不解,只能死记硬背:0正1负。
当代绝大多数计算机表示浮点数都是采用IEEE标准的,这里简化一下,我们只关心符号位,那么对于一个数,计算机其实是以下面的式子来描述它的:(-1)s×X,这里的指数s就是用来决定数值X是正数还是负数,显而易见,当s=0时,则X为正数(因为任何数的0次幂都是1),当s=1时,则X为负数(因为-1的1次幂为-1),至此我们就理解了为什么符号位为0时表示正数,为1时表示负数啦。
2 机器数
一个数在计算机中的二进制表示形式,叫做这个数的机器数。机器数是带符号的,在计算机用一个数的最高位存放符号,正数为0,负数为1.比如,十进制中整数+3,若计算机字长为8位,转换为二进制数为 0000 0000 0000 0000 0000 0000 0000 0011;如果是-3,就是1000 0000 0000 0000 0000 0000 0000 0011。
3 真值
因为第一位是符号位,所以机器数的形式值就不等于真正的数值。
例如:有符号数的真值如下
1000 0000 0000 0000 0000 0000 0000 0011的真值 = -000 0000 0000 0000 0000 0000 0000 0011 = -3;
0000 0000 0000 0000 0000 0000 0000 0011的真值 = +000 0000 0000 0000 0000 0000 0000 0011 = +3
4 原码,反码,补码的基础概念和计算方法
对于一个数,计算机要使用一定的编码方式进行存储,原码,反码,补码都是机器存储一个具体数字的编码方式。
原码
原码就是用第一位表示符号,其余位表示值,比如如果是32位二进制:
[+3]原码 = 0000 0000 0000 0000 0000 0000 0000 0011
[-3]原码 = 1000 0000 0000 0000 0000 0000 0000 0011
因为第一位是符号位,所以32位二进制整数的取值范围就是:
[1111 1111 1111 1111 1111 1111 1111 1111 , 0111 1111 1111 1111 1111 1111 1111 1111] ==> [-2147483647, 2147483647]
反码
反码的表示方法是:
*正数的反码是其本身
*负数的反码是在其原码的基础上,符号位不变,其余各个位取反
-3 原码 1000 0000 0000 0000 0000 0000 0000 0011
反码 1111 1111 1111 1111 1111 1111 1111 1100
+3 原码 0000 0000 0000 0000 0000 0000 0000 0011
反码 0000 0000 0000 0000 0000 0000 0000 0011
补码
补码的表示方法是:
*正数的补码就是其本身
*负数的补码是在其原码的基础上,符号位不变,其余各位取反,最后+1(即在反码的基础上+1)
-3 原码 1000 0000 0000 0000 0000 0000 0000 0011
反码 1111 1111 1111 1111 1111 1111 1111 1100
补码 1111 1111 1111 1111 1111 1111 1111 1101
+3 原码 0000 0000 0000 0000 0000 0000 0000 0011
反码 0000 0000 0000 0000 0000 0000 0000 0011
补码 0000 0000 0000 0000 0000 0000 0000 0011
5. 为何要使用原码, 反码和补码
计算机可以有三种编码方式表示一个数. 对于正数因为三种编码方式的结果都相同:
以下以8位机器为例
[+1] = [00000001]原 = [00000001]反 = [00000001]补
所以不需要过多解释. 但是对于负数:
[-1] = [10000001]原 = [11111110]反 = [11111111]补
可见原码, 反码和补码是完全不同的. 为何还会有反码和补码呢?
首先, 因为人脑可以知道第一位是符号位, 在计算的时候我们会根据符号位, 选择对真值区域的加减. (真值的概念在本文最开头).
但是对于计算机, 加减乘数已经是最基础的运算, 要设计的尽量简单. 计算机辨别"符号位"显然会让计算机的基础电路设计变得十分复杂! 于是人们想出了将符号位也参与运算的方法.
根据运算法则减去一个正数等于加上一个负数, 即: 1-1 = 1 + (-1) = 0 , 所以机器可以只有加法而没有减法, 这样计算机运算的设计就更简单了.
于是人们开始探索 将符号位参与运算, 并且只保留加法的方法. 首先来看原码:
计算十进制的表达式: 1-1=0
为了解决原码做减法的问题, 出现了反码:
1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在"0"这个特殊的数值上. 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.
于是补码的出现, 解决了0的符号以及两个编码的问题:
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用[1000 0000]表示-128:
(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 + [1000 0001]补 = [1000 0000]补
-1-127的结果应该是-128, 在用补码运算的结果中, [1000 0000]补 就是-128. 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示.(对-128的补码表示[1000 0000]补算出来的原码是[0000 0000]原, 这是不正确的)
使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数. 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127].
部分内容转载自别处。