数值在计算机的表现方式:
因为电路的原因,计算机只能识别1和0: 0代表低电压;而1代表高电压。
因此采用二进制的方式表示数值。
二进制数系统中,每个0或1就是一个位(bit),位是数据存储的最小单位,一个字节
等于8位,就是8个bit,这是因为早期计算机使用的编码为ASCII码,而ASCII码使用
的位数远远少于8个bit。随着计算机的快速发展,一个字节等于8位已经成为规定。
有符号和无符号数值:
无符号:
1的二进制表示 00000001
2的二进制表示 00000010
从右到左,依次为:
2^0 -> 2^1 -> 2^2 -> 2^3 -> 2^4 -> 2^5 -> 2^6 -> 2^7
如果把以上所有数排列加起来就是255种,刚好为 2^8 - 1,范围 0 ~ 255(无符号数不能是负数)
有符号数:
1的二进制表示 |0|0000001
-1的二进制表示 |1|0000001 (原码形式)
其中,最高位的|0|和|1|表示符号位(在计算机里面并没有||符号,这里为了标志一下所以加上)
因为最高位当了符号位,所以从右到左依次是:
2^0 -> 2^1 -> 2^2 -> 2^3 -> 2^4 -> 2^5 -> 2^6
把以上的数字排列加起来就是255种,因为符号位的存在,范围 -128 ~ 127(为什么会有-128?)
原码,反码以及补码:
原码:是原始的机器数表示法。用最高位表示符号位,‘1’表示负号,‘0’表示正号。其他位存放该数的二进制的绝对值。
例如:
+0 00000000 -0 10000000
+1 00000001 -1 10000001
+2 00000010 -2 10000010
................
反码: 是将原码除符号位取反的机器数表示方式(正数的反码还是其原码)。
例如:
+0 00000000 -0 11111111
+1 00000001 -1 11111110
+2 00000010 -2 11111101
...............
可以看到正数的反码就是其原码,而负数的反码就是除符号位之后将其余数值取反。
补码: 将反码加1的机器数表示方式(正数的补码还是其反码)。
例如:
+1 00000001 -1 11111111
+2 00000010 -2 11111110
...........
补码就是将得到的反码加上 1 。
好了, 为什么会出现反码和补码????整天闲着没事干对吧(计算机默认用的补码存储。)。
原码运算:
数学运算: 1 + 3 = 4
1的二进制码 00000001 ,而3则是 00000011 。
00000001
+ 00000011
————————————
00000100 = 4
数学运算: 5 + 9 = 14
5的二进制码 00000101 , 9的二进制码 00001001
00000101
+ 00001001
———————————
00001110 = 14
可以看到原码的运算非常简单,而且能满足大部分需求,再看下面一个例子:
数学运算: -1 + 1 = 0
-1的二进制码 10000001 , 1的二进制码 00000001
10000001
+ 00000001
————————————
10000010 = -2
-2 不等于 0,原码竟然不能做互补运算(一个数加上其相反数)。我的天啊!!!!
既然原码不能,那就想个办法解决一下,于是反码就出来了,因为 -0 的二进制原码为10000000,+0 的二进制原码为 00000000 ,
将 -0 的二进制码取反,变成反码 11111111 。(反码是为了负数引进的,整数的反码还是自身),如果能得到 00000000 或者 11111111的形式就解决问题!!!
最后细心的大佬发现们用反码的运算方式就可以解决,还记得反码就是原码除去符号位之后其余位数取反吗?
反码运算:
数学运算: -1 + 1 = 0
-1的反码二进制 1111110 , 1的反码二进制 00000001
11111110
+ 00000001
———————————
11111111 = -0 (反码形式)
解决了互补问题,反码真的强啊。。 开心一小会。。。。。。。。。。
再看看下面例子:
数学运算: -1 + -2 = -3
-1的反码二进制 11111110, -2的反码二进制 11111101
11111110
+ 11111101
————————————
11111011 = -4 (反码形式)
卧槽!!!怎么又不对了,自闭中!!!!!!!!!!!!!!
再想想办法!!!! !!!!!!!
反码符号取反:(此方法只是说明为什么引进补码,并不是正确的)
数学运算: -1 + -2 = -3
-1的反码二进制 11111110 , -2 的反码二进制 11111101 ,将两个负数包括符号位在内的位数全取反。
得: -1的反码符号取反二进制码 00000001 , -2 00000010
00000001
+ 00000010
——————————————
00000011 -> 将符号位改为1 -> 10000011 = -3 (负数原码形式)
再来一个:
数学运算: -126 + -1 = -127
-126的反码二进制码 10000001 , -1的反码二进制码 11111110,将两个负数包括符号位在内的位数全取反。
得: -126的反码符号取反二进制码 01111110 , -1 00000001
01111110
+ 00000001
————————————
01111111 -> 将符号位改为1 -> 11111111 = -127 (负数原码形式)
哇塞!!! 是不是瞬间就觉得可以啦。。。。。
开心多一会!!!!!!!!!.........................
!!!!!!!!!!!!! ..................
再看看下面的例子:
数学运算: -126 + -2 = -128
-126的反码二进制码 10000001 , -2的反码二进制码 11111101,将两个负数包括符号位在内的位数全取反。
得: -126的反码符号取反二进制码 01111110 , -2 00000010
01111110
+ 00000010
——————————————
10000000 -> 将符号位改为1 -> 10000000 = -0 (负数原码形式)
哭了,怎么有不对。这里因为相加之后数溢出了!!!!!!
最后一个例子:
数学运算: -2 + 3 = 1
-2反码二进制 11111101 , 3的反码二进制 00000011,将两数包括符号位在内的位数全取反。
得: -2的反码符号取反二进制码 00000010, 3 11111100
00000010
+ 11111100
——————————————
11111110 将符号位改为1 -> 11111110 = -126 (负数原码形式)
可以看见一个正数 + 一个负数 也得不到正确得结果。
由此可以看到 负数之间的减法不是主要的错误,主要是处理溢出错误和正负数之间的运算关系。
于是,大佬们都在想,既然 (正数 + 负数) 这么难搞,有没有一种方法把减法变成加法,顺便把溢出问题解决一下。。。这样说不定可以搞搞!!!经过漫长的不懈努力,补码出来了!!!补码和反码没有绝对的关系,只是反码 +1刚好等于补码!!!一定一定要记住。。
模前言:
-370 + 400 = 30 要是有个办法,让 400 回退 370 ,那就可以解决事情!!等等,好像把 -370 按某种特定规律变成一个数X ,X + 400 = 30 也能解决事情!!!!
就好像在一个操场跑步,操场是400米的,0 位置 开始跑步 ,跑了 400米之后又变成 0(位置角度从位移出发,位移0 和 400 在空间里面最终位置是一样的,所以位移是0 ),
-370 + 400 就好像 从操场逆向跑步 370米 , 这和从 400 前进 30 米完全一样!!!!!
数论—带余除法
带余除法——设x,m∈Z,m>0,则存在唯一决定的整数q和r,使得: x = qm + r ,0<r <m
用上面操场的例子说: -370 = (-1) * 400 + r ,整个叫 -370 mod 400 ,其中 r 就是余数。同样 30 mod 400 = 30 ,因为 30 = (0) * 400 + r ,很容易就得到余数为30 ,而400则称为模。 在数论模的概念中 , -370 = 30 ,因为他们的余数一样,所以可以等价 。
|-370| + 30 = 400 -> 互补数相加 = 模 (|| 为数学中绝对值符号)
模运算:
假设模为2^8次方,机器码为 100000000
数学运算: 3 + -2 = 1
因为要利用模的定义来让数值运算后能保持在 -128 ~ 127 (最高位为符号位) , 就是以模为循环,来到模位置就是来到起点位置。
-2的原码二进制为 10000010 -> 先取绝对值 -> 00000010 , 模 - 00000010 = 互补数 -> 模 -1 - 00000010 + 1 = 互补数 , ->11111111 - 00000010 + 1 = 11111110 。
+3的原码二进制位 00000011 -> 00000011 ,因为前面说过,30 mod 400 = 30,正数取模余数还是等于其本身。
11111110
+ 00000011
————————————
100000001 -> 1 (补码形式) -> 溢出的最高位1舍弃,因为刚好等于模,相当走过了一次起点
对啦!!!终于把正负之间的运算搞定了!!
再试试负数运算:
数学运算: -1 + -126 = -127
-1的原码二进制为 10000001 -> 先取绝对值 -> 00000001, 模 -1 - 00000001 + 1 = 互补数 -> 11111111 - 00000001 + 1 = 11111111。
-126的原码二进制为 11111110 -> 先取绝对值 -> 01111110 , 11111111 - 01111110 + 1 = 10000010。
11111111
+ 10000010
————————————
110000001 -> -127 (补码形式) 溢出的最高位1舍弃
好像负数间运算也OK了!!!!!!!!!!
再试试正数:
数学运算: 1 + 2 = 3
1的原码二进制为 00000001 , 2的原码二进制为 00000010 (正数取模还是本身)
00000001
+ 00000010
——————————————
00000011 -> 3 (补码形式)
正数也可以啊!!!!!!!
溢出问题主要看字节长度,短的还是会溢出,还是不可避免的,至于为什么要把减法变成加法,是因为减法器实现比加法器难多了,为了设计成本设想。
至此原码,反码,补码已经总结完了!!!,一般来说,用定义就一个数的补码非常困难,所以用反码加1会比较方便,但是反码和补码没有绝对的联系!!!
如果有讲解错误,请留言联系作者及时删除,避免引导错误。