1. 原码
原码就是符号化的数值,其编码规则简单直观:正数符号位用0表示,负数符号位用1表示,数值位保持不变。
x=+0.1101,则[x]原=0.1101;x=+1101,则[x]原=01101
x= -0.1111,则[x]原=1.1111; x= -1111,则[x]原=11111
但原码存在两个机器0,这会给数据运算带来麻烦。另外原码的加减法运算复杂,符号位不能直接参与运算。加法运算需要“同号求和,异号求差”,减法运算需要“一号求和,同好求差”,求差时还需要先比较大小,然后用大数减去小数,最后结果的符号选择也相对复杂。显然,利用原码作为机器数在实现加减法运算方面是不方便的,原码在计算机中目前仅仅用于表示浮点数的尾码。
2. 反码
反码又称1的补码,其符号位和原码相同,真值为正数时,反码和原码相同;真值为负数时,反码数值位为真值数值位取反。现代计算机中并没有采用反码进行数据表示和运算,这是因为人们找到了更好的编码——补码。
x=+0.1101,则[x]反=0.1101;x=+1101,则[x]反=01101
x= -0.1111,则[x]反=1.0000;x= -1111,则[x]反=10000
3. 补码
在计算机中,一个如果n bit 为,且需要表示正负数,那么 表示的范围是从 -2^n-1 ~ 2^n-1 -1,0就是用全0表示。负数能比正数多表达一个,是因为在计算机中,负整数是用补码表示的。所以,非常适合采用补码进行表示和运算。例如:
- 对于正整数:
正整数的原码和补码相同。
- 对于负整数:
当原码为全1的情况如下:
1111 1111 | 1111 1111 | 1111 1111 | 1111 1111(-2147483647原码)
1000 0000 | 0000 0000 | 0000 0000| 0000 0000 (-2147483647反码)
1000 0000 | 0000 0000 | 0000 0000| 0000 0001 (-2147483647补码)
由于计算机中是用补码进行计算的,所以最小负整数-2147483648补码可以表示为
1000 0000 | 0000 0000 | 0000 0000| 0000 0000
那么其原码和反码理论上是存在的,加上第一位符号位,但是32位是无法存储的。但计算机中由于直接用补码进行计算,所以可以存储其补码
x=+0.0101,则[x]补=0.0101;
x= -0.0101,则[x]补=1.1011;
x= -0.0000,则[x]补=0.0000;
x= -1.0000,则[x]补=1.0000;
- 对于 零:
此外还有两种情况没有包含进去:
+0:
0000 0000 | 0000 0000 | 0000 0000| 0000 0000 (原码、反码和补码匀为同一个)
即整数为0这种情况,在二进制中0的反码可以表示为-0和+0这两种情况
-0:
1000 0000 | 0000 0000 | 0000 0000| 0000 0000 (原码)
即-0,反码表示为:
1111 1111 | 1111 1111 | 1111 1111 | 1111 1111
-0补码为反码加1,则补码为:
1 0000 0000 | 0000 0000 | 0000 0000| 0000 0000 第一位1舍去即:
0000 0000 | 0000 0000 | 0000 0000| 0000 0000,所以+0和-0的补码是一样的,由此可见计算机为什么会使用补码进行计算。
4. 移码
移码只用于定点整数的表示,通常用于表示浮点数的阶码。其编码方式是直接将真值x加一个常数偏移量。
移码的符号位中0表示负数,1表示正数;
5. 溢出
程序验证
最后来考虑如果取int的值超过这个范围的情况,以下为程序来验证:
#include<iostream>
using namespace std;
int main()
{
int i=2147483647;
int j=2147483648;
int k=2147483649;
cout<<i<<endl;
cout<<j<<endl;
cout<<k<<endl;
return 0;
}
从上图输出结果来看,可以看到一个很有趣的结果,当正整数超出2147483647范围后出现了循环取值的现象,即2147483648溢出后回到了最小负整数-2147483648,2147483649溢出后变成了-2147483648+1=-2147483647,依次类推。
所以2147483649可以表示为-2147483648+1,1000 0000 | 0000 0000 | 0000 0000 | 0000 0000 + 0000 0000 | 0000 0000 | 0000 0000 | 0000 0001 = 1000 0000 | 0000 0000 | 0000 0000 | 0000 0001
所以int整型溢出后可以用这样的方式类推。
6. 快速计算某个补码的数值2个方法 1)把符号位也用于计算,2)翻转后加上负号如下图:
再举一个例子,1010是一个补码,计算-8+2 = -6,就是-6的补码。
或者 翻转为0101,等于6,再加上负号,为-6.
7. 快速判断两个补码相加可能会:
如果两个正数相加或负数相加,则可能会溢出,如果两个数的符号为一个为0,一个为1,则不会溢出。因此,关键是看符号位。