原码、反码、补码及其在游戏程序中的应用

原码


原码就是符号位加上真值的绝对值, 即用第一位表示符号, 其余位表示值

例如:

1的8位二进制表示:
0000 0001
-1的8位二进制表示:
1000 0001

所以8位二进制能够表示的取值范围(其实不然)[-127,127] 即:

1111 1111
0111 1111

原码是人脑最容易理解和计算的表示方式.

反码


正数的反码是其原码

负数的反码是在其原码的基础上, 符号位不变,其余各个位取反

例如:

1的8位二进制反码表示:
0000 0001
-1的8位二进制反码表示:
1111 1110

补码


正数的补码就是其原码

负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1,即在反码的基础上+1

例如:

1的8位二进制补码表示:
0000 0001
-1的8位二进制反码表示:
1111 1110
-1的8位二进制补码(在反码的基础上+1)表示:
1111 1111

已知补码求原码


正数的补码就是其原码

对于一个负数的补码求原码就是原码转换补码的逆过程

例如:

-1的8位二进制补码表示:
1111 1111
-1的8位二进制反码(在补码的基础上-1)表示:
1111 1110
-1的8位二进制原码(符号位不变,按位取反。或者先取反后+1)表示:
1000 0001

为什么要使用反码、补码


为什么要使用反码、补码?使用原码不是更易于理解吗?

使用8位原码进行减法运算

例如计算1-1:

1-1 == 1+(-1)
1的原码:0000 0001
-1的原码:1000 0001
相加:1000 0010
十进制:-2

上面的例子运算结果显然不对

为了解决原码做减法的问题, 出现了反码:

使用8位反码进行减法运算

例如计算1-1:

1-1 == 1+(-1)
1的反码:0000 0001
-1的反码:1111 1110
相加:1111 1111
原码:1000 0000
十进制:-0

发现用反码计算减法, 结果的真值部分是正确的. 而唯一的问题其实就出现在0这个特殊的数值上 虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的 而且会有[0000 0000]和[1000 0000]两个编码表示0

于是补码的出现, 解决了0的符号以及两个编码的问题:

使用8位补码进行减法运算

例如计算1-1:

1-1 == 1+(-1)
1的补码:0000 0001
-1的补码:1111 1111
相加:0000 0000
原码:0000 0000
十进制:0

这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了,而且可以用[1000 0000]表示-128

例如计算-1-127:

-1-127 == (-1) + (-127)
-1-127
-1补码1111 1111
-127补码1000 0001
相加:1000 0000

-1-127的结果应该是-128, 在用补码运算的结果中, 补码1000 0000 就是-128 但是注意因为实际上是使用以前的-0的补码来表示-128, 所以-128并没有原码和反码表示

使用补码, 不仅仅修复了0的符号以及存在两个编码的问题, 而且还能够多表示一个最低数 这就是为什么8位二进制, 使用原码或反码表示的范围为[-127, +127] 而使用补码表示的范围为[-128, 127]

程序中遇到的问题


我们知道8位二进制能够表达的取值范围是[-128, 127]

那么我们在写代码时,如果赋值时给出了能够表达的取值范围以外的数,会发生什么?

例如:

char c1 = 129;
cout << (int)c1 << endl;
char c2 = 270;
cout << (int)c2 << endl;

结果:

-127
14

首先在程序中这种行为是坚决不允许的,而且出现这样的问题也难查

我们先看看为什么是-127、14

我们先不考虑目标变量是几字节的,
先看看129、270的原码:
129: 0000 1000 0001
270: 0001 0000 1110
我们知道正数的补码就是原码,
所以129、270的补码是:
129: 0000 1000 0001
270: 0001 0000 1110
那么对于一个单字节变量来说,我们对129、270的补码取8位
c1: 1000 0001
c2: 0000 1110
而这是补码,按照上面的运算法则取8位转换成原码:
c1: 1111 1111
c2: 0000 1110
所以我们推导出结果是:
-127
14

还有一个问题我们都遇见过,就是将一个负数赋值给一个无符号整型

例如:

unsigned int unT2 = -1;

最终unT2的值是多少我们不讨论了,很简单了。

位操作在游戏程序中的应用


例如通过一个32位整型代表角色当前状态

typedef enum State
{
	E_INIT		= 0,
	E_DEAD		= 1 << 0,
	E_RIDE		= 1 << 1,
	E_FLY		= 1 << 2,
}RoleState;
class Role
{
public:
	Role()
	{
		m_nState = RoleState::E_INIT;
	}
	void AddState(RoleState ms)
	{
		m_nState |= (int)ms;
	}
	void RemoveState(RoleState ms)
	{
		m_nState &= ~((int)ms);
	}
	bool IsDead()
	{
		return m_nState & (int)(RoleState::E_DEAD);
	}
	void PrintState()
	{
		cout << "Role State " << m_nState << endl;
	}
private:
	int m_nState;
};
{
	Role objRole;
	cout << "Init State" << endl;
	objRole.PrintState();
	cout << "Add Ride Flag" << endl;
	objRole.AddState(RoleState::E_RIDE);
	objRole.PrintState();
	cout << "Role is dead? " << objRole.IsDead() << endl;
	cout << "Add Dead Flag" << endl;
	objRole.AddState(RoleState::E_DEAD);
	objRole.PrintState();
	cout << "Role is dead? " << objRole.IsDead() << endl;
	cout << "Remove Dead Flag" << endl;
	objRole.RemoveState(RoleState::E_DEAD);
	objRole.PrintState();
}

输出:

Init State
Role State 0
Add Ride Flag
Role State 2
Role is dead? 0
Add Dead Flag
Role State 3
Role is dead? 1
Remove Dead Flag
Role State 2

参考文章:https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html