原码、反码、补码定义
我们都知道计算机实际存储都是一个个二进制数字,这些数字并不是杂乱无章的,计算机会根据一定的编码方式,进行一一映射。这里讲到的原码、反码、补码就是一种编码方式,特别的是,他们都属于有符号整数的编码方式。
假设一个宽度为 ω \omega ω的二进制 x ⃗ \vec{x} x表示为:[x ω \omega ω-1,x ω \omega ω-2,…,x1,x0],下面定义下面编码方式的二进制如何转换为十进制。
- 原码
最高有效位是符号位。用来确定剩下的位应该取正还是取负。0表示正,1表示负。
B T S ( x ⃗ ) = ( − 1 ) x ω − 1 × ( ∑ i = 0 ω − 2 x i 2 i ) BTS(\vec{x}) = (-1)^{x_{\omega-1}}\times(\sum_{i=0}^{\omega-2} x_i2^i) BTS(x)=(−1)xω−1×(i=0∑ω−2xi2i)
- 反码
B T O ( x ⃗ ) = − x ω − 1 ( 2 ω − 1 − 1 ) + ∑ i = 0 ω − 2 x i 2 i BTO(\vec{x}) =-x_{\omega-1}(2^{\omega-1}-1) + \sum_{i=0}^{\omega-2} x_i2^i BTO(x)=−xω−1(2ω−1−1)+i=0∑ω−2xi2i
- 补码
B T T ( x ⃗ ) = − x ω − 1 2 ω − 1 + ∑ i = 0 ω − 2 x i 2 i BTT(\vec{x}) =-x_{\omega-1}2^{\omega-1}+ \sum_{i=0}^{\omega-2} x_i2^i BTT(x)=−xω−12ω−1+i=0∑ω−2xi2i
原码转反码、补码
假设原码值为:[0000 0001] 、[1000 0001]
- 原码转反码
正数的反码是其本身,负数的反码是在其原码的基础上, 符号位不变,其余各个位取反.
[ 00000001 ] 原 码 ⟹ [ 00000001 ] 反 码 [0000 0001]_{原码} \Longrightarrow[0000 0001]_{反码} [00000001]原码⟹[00000001]反码
[ 10000001 ] 原 码 ⟹ [ 11111110 ] 反 码 [1000 0001]_{原码} \Longrightarrow[1111 1110]_{反码} [10000001]原码⟹[11111110]反码
- 原码转补码
正数的补码就是其本身,负数的补码是在其原码的基础上,符号位不变,其余各位取反, 最后+1。 (即在反码的基础上+1)
[ 00000001 ] 原 码 ⟹ [ 00000001 ] 补 码 [0000 0001]_{原码} \Longrightarrow[0000 0001]_{补码} [00000001]原码⟹[00000001]补码
[ 10000001 ] 原 码 ⟹ [ 11111111 ] 补 码 [1000 0001]_{原码} \Longrightarrow[1111 1111]_{补码} [10000001]原码⟹[11111111]补码
无符号编码方式
像C++中的unsigned、unsigned char、unsigned short、uint32_t、uint64_t都属于无符号类型的整型数据。假设一个宽度为 ω \omega ω的二进制 x ⃗ \vec{x} x表示为:[x ω \omega ω-1,x ω \omega ω-2,…,x1,x0],无符号编码的二进制转为十进制方式如下:
B T U ( x ⃗ ) = ∑ i = 0 ω − 1 x i 2 i BTU(\vec{x}) =\sum_{i=0}^{\omega-1} x_i2^i BTU(x)=i=0∑ω−1xi2i
无符号数和有符号数之间的转换
C++中运行类型之间存在隐式转换或显示转换,当无符号数和有符号数之间存在转换时,请记住这么一句话:它也许会改变数值(编码方式),但其二进制表示不会改变。
#include <stdio.h>
int main() {
unsigned n = 4294967295; //这个类型的最大值,十六进制表示为0Xffffffff,占4个字节,即8个十六进制数字
int i = int(n);
printf("n = %u ,i = %d \n",n, i);
int j = -1;
unsigned m = unsigned(j);
printf("m = %u ,j = %d", m, j);
return 1;
}
上面代码中,n的二进制为[1111 1111 1111 1111 1111 1111 1111 1111 ],当n从无符号转为有符号的int类型的话,其二进制表示并没有改变,但其编码方式已经变成补码形式了,故其值变成-1.
位数的扩展和截断
扩展
- 无符号数的零扩展
[1111 0000]从8位扩展成12位:[0000 1111 0000]
- 补码的f扩展
[1111 0000]从8位扩展成12位:[1111 1111 0000]
#include <stdio.h>
int main() {
unsigned short us = 0xcfc7;
unsigned sl = 0x0000cfc7; //无符号数的零扩展
short sx = 0xcfc7;
int i = 0xffffcfc7; //补码的f扩展
printf("us=%u,sl=%u\n", us, sl);
printf("sx=%d,i=%d", sx, i);
return 1;
}
截断数字
- 无符号数的截断
二进制表达从宽度为 ω \omega ω截断到k,其结果为
B T U k ( x k − 1 , x k − 2 , . . . , x 0 ) = ∑ i = 0 k − 1 x i 2 i BTU_k(x_{k-1},x_{k-2},...,x_0) = \sum_{i=0}^{k-1} x_i2^i BTUk(xk−1,xk−2,...,x0)=i=0∑k−1xi2i
- 有符号的截断
B T U k ( x k − 1 , x k − 2 , . . . , x 0 ) = − x k − 1 2 k − 1 + ∑ i = 0 k − 2 x i 2 i BTU_k(x_{k-1},x_{k-2},...,x_0) = -x_{k-1}2^{k-1}+ \sum_{i=0}^{k-2} x_i2^i BTUk(xk−1,xk−2,...,x0)=−xk−12k−1+i=0∑k−2xi2i