二进制码

二进制码#

在计算机的世界中,一切都是使用二进制的数字进行表示的,约定所讨论到的二进制数字都是存储在 「机器字长」 为 8 位(即 1 Byte)的计算机中。也就是说一个存储单元最多只能够存储 8 个二进制数字,多出来的部分都将会被抛弃掉,只保留低 8 位的数据。

备注

  • 机器数 是数在计算机中的二进制表示形式。机器数是带符号的,在计算机用机器数的最高位存放符号,正数为 0,负数为 1

  • 机器数的 真值 是将带符号位的机器数转换为真正数值。

而且从硬件的角度上看,只有正数加负数才算减法,正数与正数相加,负数与负数相加,其实都可以通过 加法器 直接相加。计算机做减法,需要引入原码、反码、补码的概念。

#include <iostream>
#include <sstream>
#include <string>

原码#

  • 原码 (true form)是一种计算机中对数字的二进制定点表示方法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为 0,负数该位为 10 有两种表示:+00),其余位表示数值的大小。

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

比如:如果是 8 位二进制:

+11(10)=23+21+20=+1011(2)

这里 1011 代表真值,需要转换为 int8_t,即表示为 0000 1011

因而,int8_t 的取值范围为 [1111 1111,0111 1111],即 [127,127]

原码的特点:

  1. 原码表示直观、易懂,与真值转换容易。

  2. 原码中 0 有两种不同的表示形式,给使用带来了不便。 通常 0 的原码用 +0 表示,若在计算过程中出现了 0,则需要用硬件将 0 变成 +0

  3. 原码表示加减运算复杂。 利用原码进行两数相加运算时,首先要判别两数符号,若同号则做加法,若异号则做减法。在利用原码进行两数相减运算时,不仅要判别两数符号,使得同号相减,异号相加;还要判别两数绝对值的大小,用绝对值大的数减去绝对值小的数,取绝对值大的数的符号为结果的符号。可见,原码表示不便于实现加减运算。

反码#

原码最大的问题就在于一个数加上它的相反数不等于 0,于是反码的设计思想就是冲着解决这一点,既然一个负数是一个正数的相反数,那干脆用一个正数按位取反来表示负数。

反码的表示方法是:

  1. 正数的反码是其本身;

  2. 负数的反码是在其原码的基础上,符号位不变,其余按位取反。

比如:

11(10)=(23+21+20)=1011(2)

这里 -1011 代表真值,需要转换为 int8_t,原码表示为 1000 1011,反码为 1111 0100

反码的特点:

  1. 在反码表示中,用符号位表示数值的正负,形式与原码表示相同,即 0 为正;1 为负。

  2. 在反码表示中,数值 0 有两种表示方法。

  3. 反码的表示范围与原码的表示范围相同。

补码#

补码:正数的补码等于它的原码;负数的补码等于反码+1(这只是一种算补码的方式,多数书对于补码就是这句话)。

其实负数的补码等于反码+1只是补码的求法,而不是补码的定义,很多人以为求补码就要先求反码,其实并不是,那些计算机学家并不会心血来潮的把反码+1就定义为补码,只不过补码正好就等于反码+1而已。

补码的特点:

  1. 在补码表示中,用符号位表示数值的正负,形式与原码的表示相同,即 0 为正,1 为负。但补码的符号可以看做是数值的一部分参加运算。 正数的补码表示就是其本身,负数的补码表示的实质是把负数映像到正值区域,因此加上一个负数或减去一个正数可以用加上另一个数(负数或减数对应的补码)来代替。 从补码表示的符号看,补码中符号位的值代表了数的正确符号,0 表示正数,1 表示负数;而从映像值来看,符号位的值是映像值的一个数位,因此在补码运算中,符号位可以与数值位一起参加运算。

  2. 在补码表示中,数值 0 只有一种表示方法。

  3. 负数补码的表示范围比负数原码的表示范围略宽。纯小数的补码可以表示到-1,纯整数的补码可以表示到 2n

由于补码表示中的符号位可以与数值位一起参加运算,并且可以将减法转换为加法进行运算,简化了运算过程,因此计算机中均采用补码进行加减运算。

为什么负数的补码的求法是反码+1

因为负数的反码加上这个负数的绝对值正好等于1111,在加1,就是10000,也就是四位二进数的模,而负数的补码是它的绝对值的同余数,可以通过模减去负数的绝对值得到它的补码,所以负数的补码就是它的反码+1。

w 位补码的数学定义为:

xw12w1+i=02xi2i

“二进制上丢弃溢出的位,等价于使用溢出值取模”。

int8_t a = 11;
-100 % 2
0
std::string bin_uint8(uint8_t num)
{
  std::ostringstream oss;  // 用于记录信息
	int k;
	uint8_t *p = (uint8_t*)&num;
	for (int k = 7; k >= 0; k--) //处理 8 个位
	{
		if (*p & (1 << k))
			oss << 1;
		else
			oss << 0;
	}
	return oss.str();
}
bin_uint8(11)
"00001011"