二进制码#
在计算机的世界中,一切都是使用二进制的数字进行表示的,约定所讨论到的二进制数字都是存储在 「机器字长」 为 8 位(即 1 Byte)的计算机中。也就是说一个存储单元最多只能够存储 8 个二进制数字,多出来的部分都将会被抛弃掉,只保留低 8 位的数据。
备注
机器数 是数在计算机中的二进制表示形式。机器数是带符号的,在计算机用机器数的最高位存放符号,正数为 \(0\),负数为 \(1\)。
机器数的 真值 是将带符号位的机器数转换为真正数值。
而且从硬件的角度上看,只有正数加负数才算减法,正数与正数相加,负数与负数相加,其实都可以通过 加法器 直接相加。计算机做减法,需要引入原码、反码、补码的概念。
#include <iostream>
#include <sstream>
#include <string>
原码#
原码 (true form)是一种计算机中对数字的二进制定点表示方法。原码表示法在数值前面增加了一位符号位(即最高位为符号位):正数该位为 \(0\),负数该位为 \(1\)(\(0\) 有两种表示:\(+0\) 和 \(-0\)),其余位表示数值的大小。
原码就是符号位加上真值的绝对值,即用第一位表示符号,其余位表示值。
比如:如果是 \(8\) 位二进制:
这里 1011
代表真值,需要转换为 int8_t
,即表示为 \(0000\ 1011\)。
因而,int8_t
的取值范围为 \([1111\ 1111, 0111\ 1111]\),即 \([-127, 127]\)。
原码的特点:
原码表示直观、易懂,与真值转换容易。
原码中 \(0\) 有两种不同的表示形式,给使用带来了不便。 通常 \(0\) 的原码用 \(+0\) 表示,若在计算过程中出现了 \(-0\),则需要用硬件将 \(-0\) 变成 \(+0\)。
原码表示加减运算复杂。 利用原码进行两数相加运算时,首先要判别两数符号,若同号则做加法,若异号则做减法。在利用原码进行两数相减运算时,不仅要判别两数符号,使得同号相减,异号相加;还要判别两数绝对值的大小,用绝对值大的数减去绝对值小的数,取绝对值大的数的符号为结果的符号。可见,原码表示不便于实现加减运算。
反码#
原码最大的问题就在于一个数加上它的相反数不等于 \(0\),于是反码的设计思想就是冲着解决这一点,既然一个负数是一个正数的相反数,那干脆用一个正数按位取反来表示负数。
反码的表示方法是:
正数的反码是其本身;
负数的反码是在其原码的基础上,符号位不变,其余按位取反。
比如:
这里 -1011
代表真值,需要转换为 int8_t
,原码表示为 \(1000\ 1011\),反码为 \(1111\ 0100\)。
反码的特点:
在反码表示中,用符号位表示数值的正负,形式与原码表示相同,即 \(0\) 为正;\(1\) 为负。
在反码表示中,数值 \(0\) 有两种表示方法。
反码的表示范围与原码的表示范围相同。
补码#
补码:正数的补码等于它的原码;负数的补码等于反码+1(这只是一种算补码的方式,多数书对于补码就是这句话)。
其实负数的补码等于反码+1只是补码的求法,而不是补码的定义,很多人以为求补码就要先求反码,其实并不是,那些计算机学家并不会心血来潮的把反码+1就定义为补码,只不过补码正好就等于反码+1而已。
补码的特点:
在补码表示中,用符号位表示数值的正负,形式与原码的表示相同,即 \(0\) 为正,\(1\) 为负。但补码的符号可以看做是数值的一部分参加运算。 正数的补码表示就是其本身,负数的补码表示的实质是把负数映像到正值区域,因此加上一个负数或减去一个正数可以用加上另一个数(负数或减数对应的补码)来代替。 从补码表示的符号看,补码中符号位的值代表了数的正确符号,\(0\) 表示正数,\(1\) 表示负数;而从映像值来看,符号位的值是映像值的一个数位,因此在补码运算中,符号位可以与数值位一起参加运算。
在补码表示中,数值 \(0\) 只有一种表示方法。
负数补码的表示范围比负数原码的表示范围略宽。纯小数的补码可以表示到-1,纯整数的补码可以表示到 \(-2^n\)。
由于补码表示中的符号位可以与数值位一起参加运算,并且可以将减法转换为加法进行运算,简化了运算过程,因此计算机中均采用补码进行加减运算。
为什么负数的补码的求法是反码+1
因为负数的反码加上这个负数的绝对值正好等于1111,在加1,就是10000,也就是四位二进数的模,而负数的补码是它的绝对值的同余数,可以通过模减去负数的绝对值得到它的补码,所以负数的补码就是它的反码+1。
\(w\) 位补码的数学定义为:
“二进制上丢弃溢出的位,等价于使用溢出值取模”。
int8_t a = 11;
-100 % 2
0
std::string bin_uint8(uint8_t num)
{
std::ostringstream oss; // 用于记录信息
int k;
uint8_t *p = (uint8_t*)#
for (int k = 7; k >= 0; k--) //处理 8 个位
{
if (*p & (1 << k))
oss << 1;
else
oss << 0;
}
return oss.str();
}
bin_uint8(11)
"00001011"