一,运算规则
- 在计算机内部,有符号数是以补码的形式存储、运算的,无符号数是直接转成二进制存储、运算的。
- 有符号数和无符号数共用一套运算法则(计算机本身并不识别该数有无符号,统一处理)
- 计算机只会做加法运算,减乘除都是转换为加法来做。
- 计算机执行加法运算,是硬件电路执行一系列的位运算步骤,而不是简单的数学相加。
- 有符号数运算时,符号位会和数值位一起参与运算。
- 最高位向上进位的部分会直接舍去,只取定长位作为结果。(“定长运算”:一旦确定了数据宽度,运算器的位长就固定了,运算中产生的最高位只能丢弃(没位置了))
8位数据进行运算: 1111 1001 0000 1001 ---------------- = 1 0000 0010 最高位进位的1被直接舍弃,只取后8位0000 0010
- 机器指令中数据的表示形式是补码,汇编指令中的立即数通常也是补码形式,如果汇编指令中立即数不是补码形式,翻译为机器指令的过程中编译器也一定会将它转成补码。
1,有符号数
原码、反码、补码
原码:十进制数据的二进制表现形式,最左边是符号位,0为正,1为负
反码:正数的补码、反码是其本身,负数的反码是符号位保持不变,其余位取反
补码:正数的补码是其本身,负数的补码是在其反码的基础上+1
有负数参与运算时使用原码会导致实际值与预期值不匹配,比如-2+1,预期应该是等于-1,但是CPU若是用这两个数的原码运算,结果是-3。但是用反码运算的话,结果是正确的。
计算: -2 + 1 = -1
-2 原码:1000 0010 反码:1111 1101
+1 原码:0000 0001 反码:0000 0001
-1 原码:1000 0001 反码:1111 1110
用原码计算:
1000 0010
+ 0000 0001
---------------
= 1000 0011 得到结果是-3的原码 不正确!
用反码计算:
1111 1101
+ 0000 0001
----------------
= 1111 1110 得到结果是-1的反码 正确
使用反码可以解决部分原码形式表示的负数参与运算错误的问题,但是使用反码计算也有局限性,跨零运算会有误差1(比如-1+2结果为+0),因为+0和-0的反码不一样。
为了解决这个问题,就需要让+0和-0共用一个编码,同时负数的编码在原来反码的基础上加1,补码就诞生了,在码表中看起来就是负数反码集整体往下移动一格,-0和+0的补码统一为原来+0的反码,原来-0的反码作为现在-1的补码,原来-1的反码作为现在-2的补码,以此类推......
这样会多出来一个补码1000 0000表示-128,在一个字节的表示范围内它没有原码和反码,因此8位补码的表示范围是-128~+127。以此类推,n位补码的表示范围是~+
,计算机中有符号数是以补码的形式存储的,因此n位有符号数的表示范围是
~+
运算规则
计算机只会做加法运算,减乘除都是转换为加法来做。
- 加法运算
两符号数相加公式:[X+Y]补=[X]补 + [Y]补
机器执行加法运算时并不是这种简单的数学上的相加,而是硬件电路会执行一系列的位运算步骤,只不过达到的效果是这样的,这里就这样演示了。
计算 2+3
+2的补码:0000 0010
+3的补码:0000 0011
+5的补码:0000 0101
0000 0010
+ 0000 0011
------------------
0000 0101
- 减法运算
两有符号数相减公式:[X-Y]补 =[X]补 + [-Y]补
减法转换为加法实现。具体步骤是将[Y]补 全部位按位取反,末位加1 求出[-Y]补,然后将结果与[X]补相加。
计算 3-1=3+(-1)
+3的补码: 0000 0011
+1的补码: 0000 0001
-1的补码: 1111 1111
+2的补码: 0000 0010
-1的补码 = (+1的补码——>按位取反再加1)
0000 0011
+ 1111 1111
----------------
= 0000 0010
- 乘法运算
转换成加法运算,x*y,就是y个x相加,还是加法
- 除法运算
x/y,本质就是减法,就是x能减去多少个Y。减法能转换成加法,最终还是做的加法运算
上述演示的运算过程,只是说效果是这样的,实际上,并不是数学上的这种简单相加,计算机单是执行加法运算就有一系列的指令操作,因为计算机只会做与、或、非、异或、左(右)移等位运算,并不会数学上的什么加减乘除。具体步骤参考计算机中加减乘除运算原理——位运算。
补码最大好处就是不管是有符号数还是无符号数都可以共用一套运算规则。计算机系统本身是不识别有符号还是无符号的,它拿到的就是定长的一组二进制数据(补码),所有位一同参与运算,对有符号数和无符号数的加减法都采用一样的策略。是有符号还是无符号,是编译器来辨认的。
编译器会先将程序代码编译成汇编代码,再将汇编代码翻译成机器指令。程序代码中的数据是原码形式,机器指令中数据的表示形式是补码,汇编指令中的立即数通常也是补码形式,如果汇编指令中立即数不是补码形式,翻译为机器指令的过程中编译器也一定会将它转成补码。
比如:程序代码 X-Y 中X和Y都是原码形式
1,编译器生成的汇编代码SUB减法指令:SUB X,Y 这里的X,Y已经被转成了补码
2,汇编代码翻译成二进制机器指令,由机器执行,硬件电路实现将减数[Y]补全部位按位取反末位加1得到它的补数[-Y]补和[X]补作加法运算
3,运算的结果也是补码:[X-Y]补。
2,无符号数
表示形式、范围
无符号数是没有原码、反码和补码的,计算机是直接用二进制值表示其数值(或者说它的原码、反码、补码都是数值的二进制),没有正负号的区分。因此8位无符号数就有实实在在的8个数值位,表示范围为0~255。以此类推,n位无符号数表示范围是0~
无符号数 | 二进制 |
---|---|
0 | 0000 0000 |
1 | 0000 0001 |
2 | 0000 0010 |
3 | 0000 0011 |
4 | 0000 0100 |
... | ...... |
255 | 1111 1111 |
运算规则
计算机只会做加法运算,减乘除都是转换为加法来做,而且也不是数学上的那种简单相加。
运算规则和有符号数的一样!!计算机并不知道你是有符号还是无符号,都是按照一样的运算规则来处理。有符号还是无符号,是编译器来判断的。
- 加法运算 X + Y
X的二进制和Y的二进制相加,计算机真正执行过程没有这么简单!要达到这样的效果,有符号数运算那里也说了,实际上是执行一系列的位运算步骤。计算机中加减乘除运算原理——位运算
无符号数相加 1+3
1的二进制:0000 0001
3的二进制:0000 0011
4的二进制:0000 0100
0000 0001
+ 0000 0011
----------------
= 0000 0100
- 减法运算 X - Y = X + Y的补数
减法转换为加法实现。具体步骤是[Y]二进制 全部位按位取反,末位加1得到Y的补数,然后将结果与[X]二进制相加。
无符号数相减 3-1
3的二进制:0000 0011
1的二进制:0000 0001
1的二进制 按位取反再加1——> 1111 1111
0000 0011
+ 1111 1111
---------------
= 0000 0010 结果为2的二进制,正确
- 乘法运算
转换成加法运算,x*y,就是y个x相加,还是加法
- 除法运算
x/y,本质就是减法,就是x能减去多少个Y。减法能转换成加法,最终还是做的加法运算
二,溢出判断
什么是溢出?
正数加正数,不可能等于一个负数,如果出现了,就是溢出(上溢)。负数加负数,也不可能等于一个正数,如果出现了就是溢出(下溢)。
最高位向上进位,并不代表结果就溢出了。有符号数的运算中经常出现最高位向上进位的情况,最高位进位的部分被直接舍弃,只取定长位,只要不是上溢或下溢的情况,就不是溢出。
例如:-1+2=1
-1的补码:1111 1111
+2的补码: 0000 0010
+1的补码:0000 0001
1111 1111
+ 0000 0010
----------------
= 1 0000 0001 最高位向上进位的1被舍弃,只取后8位,结果是正确的
溢出判断标志OF
当我们进行有符号数的计算时,如果最高位和次高位产生的进位数异或结果为1,那么就发生了溢出。简单来说,就是两个进位数不同,表示发生了溢出。记录到溢出判断标志OF中。
- 次高位向最高位有进位或借位,而最高位向上无进位,则结果产生上溢。
127 + 1
预期结果是+128 (超出8位补码表示范围的上限)
实际结果是-128 (正数加正数变成负数,上溢)
+127的补码: 0111 1111
+1的补码: 0000 0001
-128的补码: 1000 0000
0111 1111
+ 0000 0001
---------------
= 1000 0000 (次高位向最高位有进位,最高位向上无进位)
2.次高位向最高位无进位或借位,而最高位向上有进位,则结果产生下溢。
-128-1=-128+(-1)
预期结果是-129 (超出8位补码表示范围)
实际结果是+128 (负数加负数变正数,下溢)
-128的补码是 1000 0000
-1的补码是 1111 1111
+128的补码是 0111 1111
1000 0000
+ 1111 1111
-----------------
= 1 0111 1111 (次高位向最高位无进位,最高位向上有进位)
无符号数的溢出判断
进位/借位标志(CF):对于无符号数的加减法,通过CF来判断是否发生了进位或借位。如果最高位产生的进位数CF和sub(sub等于1表示减法,等于0表示加法)异或结果为1,那么就发生了溢出。简单来说,就是进位数和减法操作不同,表示发生了溢出。
1,在加法运算中,如果最高位产生进位(CF=1),则表示发生了回绕,即结果超出了无符号数的表示范围。
255 + 1 加法运算
预期结果:256 超出8位无符号二进制表示范围
实际结果:0 回绕
255的二进制;1111 1111
1的二进制: 0000 0001
1111 1111
+ 0000 0001
----------------
1 0000 0000 最高位产生进位
2,在减法运算中,如果最高位产生的进位为0(即没有进位),则表示发生了溢出。
2 - 3 减法运算
预期结果:-1 超出8位无符号二进制表示范围
实际结果:255 溢出
2的二进制; 0000 0010
3的二进制: 0000 0011
3的二进制 按位取反再加1 ——> 1111 1101
0000 0010
+ 1111 1101
----------------
1111 1111 最高位无进位