计算机中有符号数的表示

news2024/11/18 21:40:03

文章目录

    • 二进制数制
      • 十进制
      • 二进制
      • 位模式
    • 基本数据类型
    • 无符号数的编码
    • 有符号数的编码
      • 原码(Sign-Magnitude)
      • 反码(Ones' Complement)
      • 补码(Two's Complement)
        • 概念导读
        • 编码格式
        • 按权展开
        • 补码加法
    • 扩展一个数字的位表示
    • 有符号数和无符号数之间的转换
    • 参考

二进制数制

所谓数制是指计数的方法。

十进制

人两手加起来共10根手指,故日常计数和做算术都使用十进制。大家熟悉并使用了一千多年的十进制起源于印度,在12世纪被阿拉伯数学家改进,并在13世纪被意大利数学家Leonardo Pisano(Fibonacci)带到西方。1、2、3的罗马计数法是Ⅰ、Ⅱ、Ⅲ,Ⅰ+Ⅱ=Ⅲ 直观展示了加法运算的含义。

数据无论使用哪种进位制,都涉及两个基本要素:基数(radix)与各数位的“位权”(weight)。
十进制数有两个特点:

  1. 用0、1、2、3、…、9这10个基本符号表示;基本数字符号(数码)的个数叫基数
  2. 遵循“逢十进一”原则,每位计满十时向高位进一。

一般地,任意一个十进制数 N 都可以表示为 ∑ i = − m n − 1 K i ∗ 1 0 i \sum_{i=-m}^{n-1}K_i\ast10^i i=mn1Ki10i

N = K n − 1 ∗ 1 0 n − 1 + K n − 2 ∗ 1 0 n − 2 + ⋯ + K 1 ∗ 1 0 1 + K 0 ∗ 1 0 0 + K − 1 ∗ 1 0 − 1 + K − 2 ∗ 1 0 − 2 + ⋯ + K − m ∗ 1 0 − m N = K_{n-1}\ast10^{n-1} + K_{n-2}\ast10^{n-2} + \cdots + K_1\ast10^1 + K_0\ast10^0 + K_{-1}\ast10^{-1} + K_{-2}\ast10^{-2} + \cdots + K_{-m}\ast10^{-m} N=Kn110n1+Kn210n2++K1101+K0100+K1101+K2102++Km10m

抛开小数部分,整数按权的展开式为:

N = ∑ i = 0 n − 1 K i ∗ 1 0 i = K n − 1 ∗ 1 0 n − 1 + K n − 2 ∗ 1 0 n − 2 + ⋯ + K 1 ∗ 1 0 1 + K 0 ∗ 1 0 0 N = \sum_{i=0}^{n-1}K_i\ast10^i = K_{n-1}\ast10^{n-1} + K_{n-2}\ast10^{n-2} + \cdots + K_1\ast10^1 + K_0\ast10^0 N=i=0n1Ki10i=Kn110n1+Kn210n2++K1101+K0100

一个数字符号在不同位时,代表的数值不同。在上述表达式中,数位 K i K_i Ki 的权为 1 0 i 10^i 10i(以基数 10 10 10为底,序号 i i i为指数),数字符号乘以其位权为这个数字符号所表示的真实数值( K i ∗ 1 0 i K_i\ast10^i Ki10i)。

二进制

在 字节存储单元及struct内存分配 中,我们介绍了二进制和以及字节存储单元。现代计算机存储和处理的信息以二值信号表示。这些微不足道的二进制数字,或者称为位(bit),形成了数字革命的基础。

对于有10根手指的人来来说,使用十进制表示法是很自然的事情,但是当构造存储和处理信息的机器时,二进制工作得更好。在计算机内部,二进制总是存放在由具有两种相反状态的存储元件构成的寄存器或存储单元中,即二进制数码0和1是由存储元件的两种相反状态来表示的。这使得二值信号很容易地被表示、存储和传输。

二值信号可以表示为导线上的高电压或低电压、晶体管的导通或截止、电子自旋的两个方向,或者顺时针或逆时针的磁场。指令集及流水线 中提到,在上个世纪的打孔编程时代,纸带上的每个孔代表一位(bit):穿孔(presence)表示1,未穿孔(absence)表示0。

十进制数按权的展开式可以推广到任意进位计数制。二进制中只有0和1两个字符,基数为2,满足“逢二进一”。权用 2 i 2^i 2i 表示,二进制的按权展开式为 N = ∑ i = 0 n − 1 K i ∗ 2 i N = \sum_{i=0}^{n-1}K_i\ast2^i N=i=0n1Ki2i

二进制与其他数制相比,有以下显著特点:

  1. 数制简单,容易基于元器件的电子特性实现数字逻辑电路。
  2. 由于二进制只有状态,因此抗干扰性强,可靠性、稳定性高。
  3. 可以基于布尔逻辑代数进行分析和综合,运算规则相对简单易实现。

基数为2的好处在于基本算术运算表很短,对比一下十进制和二进制的加法和乘法表,长短相形一目了然。

位模式

2个比特可以组合出4( 2 2 2^2 22)种状态,可表示无符号数值范围[0,3];32个比特可以组合出4294967296( 2 32 2^{32} 232)种状态,可表示无符号数值范围[0,4294967295];……。

由于一个位只能表示二元数值,所以单独一位的用处不大。当把位组合在一起,再加上某种解释(interpretasion),即赋予不同的可能位模式以含意。通常将固定位数的位串作为一个基本存储单位,这样就可以存储范围较大的值。在有限范围内的可计量数值几乎都可以用二进制数码位串组合表示,计算机的内存由数以亿万计的比特位存储单元(晶体管)组成。

大多数计算机使用8位的块,或者字节(byte),作为最小的可寻址的内存单位,而不是访问内存中单独的位(bit)。机器级程序将内存视作一个非常大的字节数组,内存的每个字节都由一个唯一的数字来标识,称为它的地址。

每个程序对象可以简单地视为一个字节块,程序本身本身就是一个字节序列(机器指令序列)。

基本数据类型

在 C++ Variable Types 中,我们总结了C/C++中的基本数据类型。C/C++语言支持多种整形数据类型——表示有限范围的整数。每种类型都用关键字来指定大小,这些关键字包括 char、short、long,参考 gnu libc Integers。C/C++语言都支持有符号(默认)和无符号数。

  1. 长短修饰符 shortlong 用于修饰整形:默认为 long 长整形,短整形需显示指定 short。
  2. 符号修饰符 signedunsigned 用于修饰字符型和整形:缺省为 signed 有符号类型,无符号需显示指定 unsigned 修饰。
  3. 当用 signed / unsignedshort / long 来修饰 int 整形时,int 可省略。

以下是字符型、短整型、整型有无符号的区分表示:

  • 有符号字符型:char/signed char;无符号字符型:unsigned char
  • 有符号短整型:short [int] /signed short [int];无符号短整型:unsigned short [int]
  • 有符号整型:int /signed [int];无符号整型:unsigned [int]

无符号数的编码

无符号(unsigned)编码基于传统的二进制表示法,表示大于或等于0的非负数。

假设有一个整数数据类型有w位。我们可以将位向量写成 x ⃗ \vec{x} x 表示整个向量,或者写成 [ x w − 1 , x w − 2 , ⋯   , x 0 ] [x_{w-1}, x_{w-2}, \cdots, x_0] [xw1,xw2,,x0],表示向量中的每一位。把 x ⃗ \vec{x} x 看成一个二进制表示的数,就获得了 x ⃗ \vec{x} x 的无符号表示。在这个二进制编码中,每个位 x i x_i xi 都取值0或1,后一种取值意味着数值 2 i 2^i 2i 应为数字值的一部分。我们用一个函数 B 2 U w B2U_w B2Uw(Binary to Unsigned的缩写,长度为 w w w)来表示。

对位向量 x ⃗ = [ x w − 1 , x w − 2 , ⋯   , x 0 ] \vec{x} = [x_{w-1}, x_{w-2}, \cdots, x_0] x =[xw1,xw2,,x0]

B 2 U w ( x ⃗ ) = ˙ ∑ i = 0 w − 1 x i ∗ 2 i B2U_w(\vec{x}) \dot=\sum_{i=0}^{w-1}x_i\ast2^i B2Uw(x )=˙i=0w1xi2i

在这个等式中,等号“ = ˙ \dot= =˙”表示左边被定义为等于右边。函数 B 2 U w B2U_w B2Uw 将一个长度为 w w w 的0、1串映射到非负整数。

w w w位所能表示的最小值用位向量 [ 00 ⋯ 0 ] [00\cdots0] [000](全零)表示,也就是整数值0,而最大值是用位向量 [ 11 ⋯ 1 ] [11\cdots1] [111](全1),也就是整数值 U M a x w = ˙ ∑ i = 0 w − 1 x i ∗ 2 i = 2 w − 1 UMax_w\dot=\sum_{i=0}^{w-1}x_i\ast2^i=2^w-1 UMaxw=˙i=0w1xi2i=2w1。即 w w w位二进制位串所能表示的数值范围是 [ 0 , 2 w − 1 ] [0, 2^w-1] [0,2w1]

在主流64位LP64实现中,unsigned char、unsigned short [int]、unsigned [int]、unsigned long [int](或 long unsigned [int])分别占1、2、4、8个字节(8、16、32、64位)。无符号字符(unsigned char)占用1个字节(8位),所能表示的数值范围是 [ 0 , 2 8 − 1 ] = [ 0 , 255 ] [0, 2^8-1] = [0, 255] [0,281]=[0,255]

有符号数的编码

对于很多应用,我们还希望表示负数值。在计算机中,如何表示符号呢?
在计算机中,对于数的符号(正号+和负号-)也只能用0和1这两位数字表示。通常用一个数的最高位作为符号位,最高位为0表示符号位为正;最高位为1表示符号位为负。这样,数的符号标识也“数码化”了。即带符号数的数值和符号统一用二进制数码形式来表示。

在将数的符号用数码(0或1)表示后,数值部分究竟是保留原来的形式,还是按一定的规则做某些变化,这要取决于运算方法的需要,从而有四种常见的机器数形式:原码反码补码移码

为了区别原来的数与它在机器中的表示形式,将一个数(连同符号)在机器中加以数码化后的形式,称为机器数或机器码,而把机器数所代表的实际数值称为真值

原码(Sign-Magnitude)

原码表示法比较直观,其数值部分保留其真值(的绝对值)。
原码

  • 8位二进制原码表示的数值范围为 11111111-10000000,00000000-01111111,即 -127 - -0,+0 - +127。其中,“0”有-0和+0之分, [ − 0 ] 原 = 10000000 [-0]_原=1 0000000 [0]=10000000 [ + 0 ] 原 = 00000000 [+0]_原=0 0000000 [+0]=00000000

例如,正数89的二进制表示为 + 1011001 +1011001 +1011001,其原码表示为:
01011001
负数-89的二进制表示为 − 1011001 -1011001 1011001,其原码表示为:
11011001

原码表示法的优点是比较直观、简单易懂,后面在浮点数中有使用到原码编码。
原码的符号位不是数值的一部分,不能直接参与运算,导致加法运算复杂。为了解决这些矛盾,引入了反码和补码。

反码(Ones’ Complement)

对于正数而言,其反码形式与其原码相同:最高位为符号位,用0表示正数,其余位为数值位不变。对于负数而言,其反码表示为:最高位符号位为1,其余数值位在原码的基础上按位取反。反码在机器中的表示形式如下:
反码

  • 8位二进制反码表示的数值范围为 10000000-11111111,00000000-01111111(负数数值部分求反复原:11111111-10000000,00000000-01111111),即 -127 - -0,+0 - +127。其中,“0”有-0和+0之分, [ − 0 ] 反 = 11111111 [-0]_反=1 1111111 [0]=11111111 [ + 0 ] 反 = 00000000 [+0]_反=0 0000000 [+0]=00000000

-89的二进制表示为 − 1011001 -1011001 1011001,其原码表示为 11011001 1 1011001 11011001,则其反码表示为 10100110 1 0100110 10100110
在这里插入图片描述
将反码表示规则用表达式形式定义如下:

[ X ] 反 = { X , X > 0 或 X = + 0 ( 2 n − 1 ) − ∣ X ∣ , X < 0 或 X = − 0 % 反码 [X]_反= \begin{cases} X, & X>0 或 X=+0 \\ (2^n-1)- \lvert X \rvert, & X<0 或 X=-0 \end{cases} [X]={X,(2n1)X,X>0X=+0X<0X=0

当 X<0 或 X=-0 时,按照无符号数解析位向量, ∣ X ∣ + [ X ] 反 → = 2 n − 1 \lvert X \rvert + \overrightarrow{[X]_反}=2^n-1 X+[X] =2n1。例如当n=8时, ∣ X ∣ + [ X ] 反 → = 11111111 = 255 \lvert X \rvert + \overrightarrow{[X]_反}=11111111=255 X+[X] =11111111=255

虽然过去生产过基于反码表示的机器,但是几乎所有的现代机器都使用补码形式表示有符号整数。现在通常已不再单独使用反码,而主要是作为求补码的一个中间步骤来使用。

补码(Two’s Complement)

在计算机中,最常见的有符号(整)数的表示方式是补码。采用补码运算可以将减法变成补码加法运算,在微处理器中只需加法的电路就可以实现加法、减法运算。

概念导读

为了理解补码的概念,我们先来看看圆周运动的例子。在现实生活中,一个圆周的可视角度度量为0-2π(或0°-360°),以原点为起点的射线OA逆时针旋转一圈的弧度为2π。

圆周运动具有周期性,即具有“周而复始”的变化规律。假设OA的初始弧度为α,终边OA每绕原点旋转一周,α增加2π弧度(旋转k周后,弧度变成α+k·2π),但OA位置不变。由三角函数的定义可知,终边相同的角的同一三角函数的值相等。

sin ⁡ ( α + k ⋅ 2 π ) = sin ⁡ α cos ⁡ ( α + k ⋅ 2 π ) = cos ⁡ α tan ⁡ ( α + k ⋅ 2 π ) = tan ⁡ α \begin{gather*} \sin(\alpha+k·2\pi) = \sin\alpha \\ \cos(\alpha+k·2\pi) = \cos\alpha \\ \tan(\alpha+k·2\pi) = \tan\alpha \end{gather*} sin(α+k2π)=sinαcos(α+k2π)=cosαtan(α+k2π)=tanα

由上面的公式可知,可以把求任意角的三角函数值,转化为求0-2π(或0°-360°)角的三角函数值。射线OA逆时针旋转π和顺时针旋转π(-π)的终边是相同的,逆时针旋转5π/3和顺时针旋转π/3(-π/3)的终边是相同的。以下将负弧度的正弦计算转化到0-2π区间换算:

sin ⁡ ( − π + 2 π ) = sin ⁡ π sin ⁡ ( − π 3 + 2 π ) = sin ⁡ 5 π 3 \begin{gather*} \sin(-\pi+2\pi) = \sin\pi \\ \sin(-\frac{\pi}{3}+2\pi) = \sin\frac{5\pi}{3} \end{gather*} sin(π+2π)=sinπsin(3π+2π)=sin35π


我们再来进一步讨论日常生活中校正时钟的例子。假定时钟停在7点,而正确时间是5点,要拨准时钟可以有两种不同的拨法:倒拨2个格(时光倒流2h),顺拨10个格(穿越到未来10h后)。想象一下,龟兔在7点钟刻度背向而行,假设兔子的速度是乌龟的5倍,兔子顺时针跑10格,乌龟逆时针跑2格,它们将在5点刻度处迎面相遇。

倒拨2个格,即7-2=5(做减法);顺拨10个格,即7+10=12+5=5(做加法,钟面上12=0)。这就表明,在舍掉进位的情况下,“从7减去2”和“往7加上10”所得的结果是一样的。而2和10的和恰好等于模数12。我们把10称为-2对于模数的补数。

之所以顺拨(做加法)与倒拨(做减法)的结果相同,是由于钟面的容量有限,其刻度是是十二进制,超过12以后又从零开始计数,自然丢失了12。此处12是溢出量,又称为模(mod)。在圆周运动中,每转动一周的2π弧度可视为溢出量(模)。

在电脑和手机的日期和时间偏好设置中,通常可以设置显示24小时,因为地球自转一圈是一天(接近24h)。24h制的1点和13点都对应钟表上的1点。“天天向上”、“日复一日”中的“天”和“日”即为溢出量,每过24h(模)又将开启崭新的一天。

计算机中的运算受一定字长的限制,它的运算部件与寄存器都有一定的位数,因而在运算过程中也会产生溢出量,所产生的溢出量实际上就是模。可见,计算机的运算也是一种有模运算

编码格式

对于正数而言,其补码形式与其原码、反码相同:最高位为符号位,用0表示正数,其余位为数值位不变。对于负数而言,其补码表示为:最高位符号位为1,其余数值位在原码的基础上按位取反并加1(反码+1)。补码在机器中的表示形式如下:
补码
-89的二进制表示为 − 1011001 -1011001 1011001,其原码表示为 11011001 1 1011001 11011001,其反码表示为 10100110 1 0100110 10100110,则其补码表示为 10100111 1 0100111 10100111
补码

  • 8位二进制补码表示的数值范围为 10000000-11111111,00000000-01111111,即 -128 - -1,+0 - +127。其中,无-0和+0之分,保证了0的唯一性。另外,取值范围为连贯区间 [-128, 127],包含128个负数、128个非负数。负数、非负数各占一半,负数比正数多一个。

将补码表示规则用表达式形式定义如下:

[ X ] 补 = { X , X ≥ 0 2 n − ∣ X ∣ ( 2 n + X ) , X < 0 % 补码 [X]_补= \begin{cases} X, & X\ge0 \\ 2^n- \lvert X \rvert(2^n+X), & X<0 \end{cases} [X]={X,2nX2n+X,X0X<0

当 X<0 时,按照无符号数解析位向量, ∣ X ∣ + [ X ] 补 → = 2 n \lvert X \rvert+\overrightarrow{[X]_补}=2^n X+[X] =2n。例如当n=8时, ∣ X ∣ + [ X ] 补 → = 11111111 + 1 = 256 \lvert X \rvert+\overrightarrow{[X]_补}=11111111+1=256 X+[X] =11111111+1=256

-89的二进制补码表示为 10100111 1 0100111 10100111,按照无符号数解析位向量的值为167,满足以下:

167 = 256 − ∣ − 89 ∣ ∣ − 89 ∣ + 167 = 256 \begin{gather} 167=256 - \lvert -89 \rvert \\ \lvert -89 \rvert + 167 = 256 \end{gather} 167=2568989+167=256

按权展开

-89的二进制表示为 − 1011001 -1011001 1011001,其原码表示为 11011001 1 1011001 11011001,其反码表示为 10100110 1 0100110 10100110,则其补码表示为 10100111 1 0100111 10100111

∣ − 89 ∣ \lvert -89 \rvert 89 的二进制位向量为 01011001 01011001 01011001

  • 反码数值部分位向量= ( 2 n − 1 − 1 ) − ∣ X ∣ = 127 − ∣ − 89 ∣ = 0 b 01111111 − 0 b 01011001 = 0 b 00100110 (2^{n-1}-1)-\lvert X \rvert = 127-\lvert -89 \rvert=0b01111111-0b01011001=0b00100110 (2n11)X=12789=0b011111110b01011001=0b00100110
  • 补码数值部分位向量= ( 2 n − 1 − 1 ) − ∣ X ∣ + 1 = 2 n − 1 − ∣ X ∣ = 127 − ∣ − 89 ∣ + 1 = 0 b 01111111 − 0 b 01011001 + 1 = 0 b 00100111 (2^{n-1}-1)-\lvert X \rvert+1 = 2^{n-1}-\lvert X \rvert = 127-\lvert -89 \rvert+1=0b01111111-0b01011001+1=0b00100111 (2n11)X+1=2n1X=12789+1=0b011111110b01011001+1=0b00100111

补码高位符号位占1位,其余数值部分占 n-1 位。当将最高符号位解释为负权( 2 n − 1 2^{n-1} 2n1)时,整体位向量刚好可计算出原始负值:

− 2 n − 1 + ( 2 n − 1 − ∣ X ∣ ) = − ∣ X ∣ -2^{n-1} + (2^{n-1}-\lvert X \rvert) = -\lvert X \rvert 2n1+(2n1X∣)=X

我们用一个函数 B 2 T w B2T_w B2Tw(Binary to Two’s Complement 的缩写,长度为 w w w)来表示位向量 x ⃗ = [ x w − 1 , x w − 2 , ⋯   , x 0 ] \vec{x} = [x_{w-1}, x_{w-2}, \cdots, x_0] x =[xw1,xw2,,x0] 到补码的编码映射:

B 2 T w ( x ⃗ ) = ˙ − x w − 1 ∗ 2 w − 1 + ∑ i = 0 w − 2 x i ∗ 2 i B2T_w(\vec{x}) \dot=-x_{w-1}\ast2^{w-1} + \sum_{i=0}^{w-2}x_i\ast2^i B2Tw(x )=˙xw12w1+i=0w2xi2i

最高有效位 x w − 1 x_{w-1} xw1 称为符号位,它的“权重”为 − 2 w − 1 -2^{w-1} 2w1,是无符号表示中权重的负数。符号位被设置为1时,表示值为负,而当设置为0时,值为非负。

∣ − 89 ∣ \lvert -89 \rvert 89 的二进制位向量为 01011001 01011001 01011001,-89的二进制补码表示为 10100111 1 0100111 10100111,可基于 B 2 T w B2T_w B2Tw 函数按权展开复原补码的真值:

B 2 T 8 ( [ 01011001 ] ) = − 0 ∗ 2 7 + 1 ∗ 2 6 + 1 ∗ 2 4 + 1 ∗ 2 3 + 1 ∗ 2 0 = − 0 + 64 + 16 + 8 + 1 = 89 B 2 T 8 ( [ 10100111 ] ) = − 1 ∗ 2 7 + 1 ∗ 2 5 + 1 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = − 128 + 32 + 4 + 2 + 1 = − 89 B2T_8([0 1011001]) = -0\ast2^7+1\ast2^6+1\ast2^4+1\ast2^3+1\ast2^0=-0+64+16+8+1=89 \\ B2T_8([1 0100111]) = -1\ast2^7+1\ast2^5+1\ast2^2+1\ast2^1+1\ast2^0=-128+32+4+2+1=-89 B2T8([01011001])=027+126+124+123+120=0+64+16+8+1=89B2T8([10100111])=127+125+122+121+120=128+32+4+2+1=89

让我们再来基于 B 2 T w B2T_w B2Tw 展开式,重新推导一下 w w w 位补码所能表示的取值范围。

  • 最小值是位向量 [ 10 ⋯ 0 ] [10\cdots0] [100],只设置负权,其他正权位清零,此种情形负得最多,其整数值为 T M i n w = ˙ − 2 w − 1 TMin_{w}\dot=-2^{w-1} TMinw=˙2w1
  • 当设置了负权时,设置其他所有正权位,位向量为 [ 11 ⋯ 1 ] [11\cdots1] [111]。此种情形负得最少,其整数值为 − 2 w − 1 + ∑ i = 0 w − 2 x i ∗ 2 i = − 2 w − 1 + ( 2 w − 1 − 1 ) = − 1 -2^{w-1}+\sum_{i=0}^{w-2}x_i\ast2^i=-2^{w-1}+(2^{w-1}-1)=-1 2w1+i=0w2xi2i=2w1+(2w11)=1,即最大负整数。
  • 最大值是位向量 [ 01 ⋯ 1 ] [01\cdots1] [011],清除负权(即为非负数),清除其他所有正权位,其值为0;若设置其他所有正权位,其整数值为 T M a x w = ˙ ∑ i = 0 w − 2 x i ∗ 2 i = 2 w − 1 − 1 TMax_{w}\dot=\sum_{i=0}^{w-2}x_i\ast2^i=2^{w-1}-1 TMaxw=˙i=0w2xi2i=2w11

w = 8 w=8 w=8为例,一个字节(byte)的补码编码所能表示数值范围是 [ − 2 8 − 1 , 2 8 − 1 − 1 ] [-2^{8-1}, 2^{8-1}-1] [281,2811],即 [ − 128 , 127 ] [-128, 127] [128,127],包含128个负数(-128 - -1)、128个非负数(0 - 127)。

补码加法

计算机的表示法使用有限数量的位对一个数字编码,当结果太大以至不能表示时,某些运算就会溢出(overflow)。因此,我们说计算机运算也是一种有模运算。当然,在计算机中不是像上述时钟例子那样以12为模,在定点小数的补码表示中是以 2 2 2 为模,在定点整数中则以 2 n 2^n 2n 为模(n=8,16,32,64,…)。

计算机中用补码表示法编码有符号数,把负数用补码表示。减去一个正数可以看成加上一个负数,这样在计算机中不用单独设置减法器,而是基于补码一律按加法运算规则实现减法的等效计算。

回想调拨钟表的例子,2和10的和恰好等于模数12,我们把10称为-2对于mod12的补数。其含义是在钟表盘上,逆时针回拨2格等效于顺时针拨动10格。

【例1】已知X=+0000111(7),Y=-0010011(-19),求两数的补码之和。

  • [ X ] 补 = 00000111 , [ Y ] 补 = 11101101 [X]_补=0 0000111,[Y]_补=1 1101101 [X]=00000111[Y]=11101101,人工计算 Z=X+Y=7+(-19)=-12(补码为11110100)。

若将 [ Y ] 补 → \overrightarrow{[Y]_补} [Y] 也视作无符号数=237,将计算结果Z也直视为无符号数 [ Z ] 补 → \overrightarrow{[Z]_补} [Z] =244,满足 7+237=244。244恰为-12对于mod256的补数。

设想一个有256个刻度的大笨钟,现在在刻度7处,逆时针回拨19格和顺时针拨动237格,效果都是拨到刻度244处。当采用补码表示时,表盘的256个刻度被划分成左边逆时针半盘[-128,-1]和右边顺时针半盘[0,127],原先的244刻度点映射为新的逆时针-12点。

在钟表盘刻度范围内,逆时针的减法和顺时针的加法,最终达到同一刻度点。在模数(例如mod256)范围内,减法可以视作加补码(计算反码本身已经做了减法运算),补码可以视作无符号数直接参与竖式按位加计算。另一方面,由于操作数和运算结果都统一用补码表示,补码的最高符号位统一按照负权解释,即补码的符号位可视作整体数值的一部分。从这个角度讲,符号位直接参与运算貌似也是解释得通。

接下来,我们重点看看补码加法的溢出问题及溢出判断。

【例2】已知X=+1000000(64),Y=+1000001(65),求两数的补码之和。

  • 直接对补码列竖式计算,两个正数相加,结果为负数!?补码之和是129,超出了 [0, 127],即产生了溢出现象。此时,数值部分向符号位产生进位 C 0 = 1 C_0=1 C0=1,符号位未向高位产生进位 C f = 0 C_f=0 Cf=0

        0   1 ( C f C 0 )       [ X ] 补 0   1000000 ( + 64 的补码 ) + ) [ Y ] 补 0   1000001 ( + 65 的补码 )   1   0000001 129 \begin{array}{c|lcr} \:\:\:\:\: & \: \quad 0\ 1 \qquad\qquad\qquad (C_fC_0)\\ \:\:\:\:\: [X]_补 & \qquad 0\ 1000000 \qquad (+64的补码) \\ +) [Y]_补 & \qquad 0\ 1000001 \qquad (+65的补码) \\ \hline \ & \qquad 1\ 0000001 \qquad 129 \end{array} [X]+)[Y] 0 1(CfC0)0 1000000(+64的补码)0 1000001(+65的补码)1 0000001129

用以下C语言代码测试验证,计算结果Z的位向量为0x81(即无符号数129),补码对应的真值=-127。

    signed char X = 64;
    signed char Y = 65;
    signed char Z = X+Y;
    printf("Z=0x%hhx, %hhd\n", Z, Z);

【例3】已知X=-1111111(-127),Y=-0000010(-2),要求进行补码的加法运算。

  • 直接对补码列竖式计算,两个负数相加,结果为正数!?预期的结果-129超出了8位补码所能表示的负数范围 [-128, -1],即产生了溢出现象。此时,数值部分向符号位未产生进位 C 0 = 0 C_0=0 C0=0,符号位向高位产生进位 C f = 1 C_f=1 Cf=1

        1   0 ( C f C 0 )       [ X ] 补 1   0000001 ( − 127 的补码 ) + ) [ Y ] 补 1   1111110 ( − 2 的补码: 254 )   0   1111111 127 \begin{array}{c|lcr} \:\:\:\:\: & \: \quad 1\ 0 \qquad\qquad\qquad (C_fC_0)\\ \:\:\:\:\: [X]_补 & \qquad 1\ 0000001 \qquad (-127的补码) \\ +) [Y]_补 & \qquad 1\ 1111110 \qquad (-2的补码:254) \\ \hline \ & \qquad 0\ 1111111 \qquad 127 \end{array} [X]+)[Y] 1 0(CfC0)1 0000001(127的补码)1 1111110(2的补码:254)0 1111111127

用以下C语言代码测试验证,计算结果Z的位向量为0x7f,符号位溢出为0,补码对应的真值即无符号值127。

    signed char X = -127;
    signed char Y = -2;
    signed char Z = X+Y;
    printf("Z=0x%hhx, %hhd\n", Z, Z);

【例4】已知X=-0000011(-3),Y=-0000010(-2),要求进行补码的加法运算。

  • 直接对补码列竖式计算,直接补码运算产生的结果的无符号数值是251,正是预期结果-5的补码!此时,数值部分向符号位产生进位 C 0 = 1 C_0=1 C0=1,符号位向高位产生进位 C f = 1 C_f=1 Cf=1

        1   1 ( C f C 0 )       [ X ] 补 1   1111101 ( − 3 的补码: 253 ) + ) [ Y ] 补 1   1111110 ( − 2 的补码: 254 )   1   1111011 ( − 5 的补码: 251 ) \begin{array}{c|lcr} \:\:\:\:\: & \: \quad 1\ 1 \qquad\qquad\qquad (C_fC_0)\\ \:\:\:\:\: [X]_补 & \qquad 1\ 1111101 \qquad (-3的补码:253) \\ +) [Y]_补 & \qquad 1\ 1111110 \qquad (-2的补码:254) \\ \hline \ & \qquad 1\ 1111011 \qquad (-5的补码:251) \end{array} [X]+)[Y] 1 1(CfC0)1 1111101(3的补码:253)1 1111110(2的补码:254)1 1111011(5的补码:251)


以上例1和例4中,8位正数之和、8位负数之和未超出8位补码表示的数值范围时,没有产生溢出,结果正确。此时,满足 C 0 = C f C_0=C_f C0=Cf,同时为0或同时为1。
以上例2和例3中,8位正数之和、8位负数之和超出8位补码表示的数值范围时,产生了溢出,结果错误。此时,满足 C 0 ≠ C f C_0 \ne C_f C0=Cf,一个为0且一个为1。

综合以上,可用下列逻辑表达式进行补码加法的溢出判断:

O F = C f ⊕ C 0 OF = C_f \oplus C_0 OF=CfC0

扩展一个数字的位表示

一个常见的运算是在不同字长的整数之间转换,同时又保持数值不变。考虑从一个较小的数据类型转换到一个较大的类型,即扩大位宽。

要将一个无符号数转换成一个更大的数据类型时,只要简单地在开头添加0。具体来说,原数值的字节保持在低(权)位不变,在新增的高(权)位补0。

要将一个补码数字转换为一个更大的数据类型,可以执行一个符号扩展(sign extension)。具体来说,原数值的字节保持在低(权)位不变,在新增的高(权)位复制符号位。

  1. 对于正数,高位复制符号位0,不影响其真值。
  2. 对于负数,高位复制符号位1,也不影响真值。

考虑负数的补码位向量 x ⃗ = [ x w − 1 , x w − 2 , ⋯   , x 0 ] \vec{x} = [x_{w-1}, x_{w-2}, \cdots, x_0] x =[xw1,xw2,,x0],其编码映射如下:

B 2 T w ( x ⃗ ) = ˙ − x w − 1 ∗ 2 w − 1 + ∑ i = 0 w − 2 x i ∗ 2 i B2T_w(\vec{x}) \dot=-x_{w-1}\ast2^{w-1} + \sum_{i=0}^{w-2}x_i\ast2^i B2Tw(x )=˙xw12w1+i=0w2xi2i

当复制高符号位 x w − 1 x_{w-1} xw1 时,位向量 x ′ ⃗ = [ x w − 1 , ⋯   , x w − 1 , x w − 1 , x w − 2 , ⋯   , x 0 ] \vec{x'} = [x_{w-1}, \cdots, x_{w-1}, x_{w-1}, x_{w-2}, \cdots, x_0] x =[xw1,,xw1,xw1,xw2,,x0],则有 B 2 T w ( x ⃗ ) = B 2 T w ′ ( x ′ ⃗ ) B2T_w(\vec{x}) = B2T_{w'}(\vec{x'}) B2Tw(x )=B2Tw(x )


以-89为例,其8位补码位向量为 [ 10100111 ] [1 0100111] [10100111],将其符号扩展为16位的位向量位 [ 1111111110100111 ] [11111111 10100111] [1111111110100111]

B 2 T 8 ( [ 10100111 ] ) = − 1 ∗ 2 7 + 1 ∗ 2 5 + 1 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = − 128 + 32 + 4 + 2 + 1 = − 89 B 2 T 16 ( [ 1111111110100111 ] ) = − 1 ∗ 2 15 + 1 ∗ 2 14 + ⋯ + 1 ∗ 2 8 + 1 ∗ 2 7 + 1 ∗ 2 5 + 1 ∗ 2 2 + 1 ∗ 2 1 + 1 ∗ 2 0 = − 128 + 32 + 4 + 2 + 1 = − 89 \begin{aligned} & B2T_8([1 0100111]) = -1\ast2^7+1\ast2^5+1\ast2^2+1\ast2^1+1\ast2^0=-128+32+4+2+1=-89 \\ & B2T_{16}([11111111 10100111]) =-1\ast2^{15}+1\ast2^{14}+\cdots+1\ast2^8+1\ast2^7+1\ast2^5+1\ast2^2+1\ast2^1+1\ast2^0=-128+32+4+2+1=-89 \end{aligned} B2T8([10100111])=127+125+122+121+120=128+32+4+2+1=89B2T16([1111111110100111])=1215+1214++128+127+125+122+121+120=128+32+4+2+1=89

我们设补码除符号位后的数值位值为v,则

B 2 T 8 ( [ 10100111 ] ) = − 1 ∗ 2 7 + v B 2 T 16 ( [ 1111111110100111 ] ) = − 1 ∗ 2 15 + 1 ∗ 2 14 + ⋯ + 1 ∗ 2 8 + 1 ∗ 2 7 + v \begin{aligned} & B2T_8([1 0100111]) =-1\ast2^7+v \\ & B2T_{16}([11111111 10100111]) =-1\ast2^{15}+1\ast2^{14}+\cdots+1\ast2^8+1\ast2^7+v \end{aligned} B2T8([10100111])=127+vB2T16([1111111110100111])=1215+1214++128+127+v

更进一步有:

B 2 T 16 ( [ 1111111110100111 ] ) = 2 8 ( − 2 7 + 2 6 + ⋯ + 2 0 ) + 2 7 + v = 2 8 ( − 2 7 + ( 2 7 − 1 ) ) + 2 7 + v = − 2 8 + 2 7 + v = − 2 7 + v \begin{aligned} B2T_{16}([11111111 10100111]) & = 2^8(-2^7+2^6+\cdots+2^0)+2^7+v \\ & = 2^8(-2^7+(2^7-1))+2^7+v \\ & = -2^8+2^7+v \\ & = -2^7+v \end{aligned} B2T16([1111111110100111])=28(27+26++20)+27+v=28(27+(271))+27+v=28+27+v=27+v

对比可知,符号扩展前后的补码所表示的真值不变。

有符号数和无符号数之间的转换

C语言允许在各种不同的数据类型之间做强制类型转换。例如,假设变量x声明为int,u声明为unsigned。表达式 (unsigned)x 会将x的值转换为一个无符号数值,而 (int)u 将u的值转换为一个有符号整数。

考虑以下代码:

    short int v = -12345;
    unsigned short uv = (unsigned short)v;
    printf("v=%hd, uv=%hu\n", v, uv);
    printf("v=0x%hx, uv=0x%hx\n", v, uv);

在一台采用补码的机器上,上述代码运行输出:

v=-12345, uv=53191
v=0xcfc7, uv=0xcfc7

可以看到,强制类型转换的结果保持位向量(位模组)不变,只是改变了解释这些位的方式。
v=-12345 的补码表示和 uv=53191 的无符号表示是完全一样的。

以上代码片段示例演示了负数补码转换成无符号数。对于负数,补码中最高位作为符号位,本来解释为负权 − 2 w − 1 -2^{w-1} 2w1,按照无符号解释为正权数值位 2 w − 1 2^{w-1} 2w1。由于后面位的数值 v v v 保持不变,相当于在补码表示的真值基础上加上 2 w 2^w 2w

对满足 T M i n w ≤ x ≤ T M a x w TMin_w \le x \le TMax_w TMinwxTMaxw x x x 有:

T 2 U w ( x ) = { x , x ≥ 0 2 w + x ( 2 w − ∣ x ∣ ) , x < 0 T2U_w(x)= \begin{cases} x, & x\ge0 \\ 2^w+x(2^w- \lvert x \rvert), & x<0 \end{cases} T2Uw(x)={x,2w+x2wx,x0x<0

简单推导如下:

B 2 U w ( x ) − B 2 T w ( x ) = ( 2 w − 1 + v ) − ( − 2 w − 1 + v ) = 2 ∗ 2 w − 1 = 2 w    ⟹    B 2 U w ( x ) = B 2 T w ( x ) + 2 w \begin{aligned} B2U_w(x) - B2T_w(x) & = (2^{w-1}+v)-(-2^{w-1}+v) = 2\ast2^{w-1}=2^w \\ & \implies B2U_w(x) = B2T_w(x) + 2^w \end{aligned} B2Uw(x)B2Tw(x)=(2w1+v)(2w1+v)=22w1=2wB2Uw(x)=B2Tw(x)+2w

例如: T 2 U 16 ( − 12345 ) = − 12345 + 2 16 = 53191 T2U_{16}(-12345) = -12345+2^{16}=53191 T2U16(12345)=12345+216=53191


同理,无符号数的最高位解释为正权数值位 2 w − 1 2^{w-1} 2w1,当将其位向量解释成补码时,最高位将作为符号位,解释为负权 − 2 w − 1 -2^{w-1} 2w1。由于后面位的数值 v v v 保持不变,相当于在无符号真值基础上减去 2 w 2^w 2w

对满足 0 ≤ u ≤ U M a x w 0 \le u \le UMax_w 0uUMaxw u u u 有:

U 2 T w ( u ) = { u , u ≤ T M a x w u − 2 w , u > T M a x w U2T_w(u)= \begin{cases} u, & u \le TMax_w \\ u - 2^w, & u>TMax_w \end{cases} U2Tw(u)={u,u2w,uTMaxwu>TMaxw

对于 w = 8 w=8 w=8,当 u ≤ T M a x w = 127 u \le TMax_w = 127 uTMaxw=127 时,即 0 ≤ u ≤ 127 0 \le u \le 127 0u127,刚好落入8位补码所能表示的非负数区间 ;当 u > T M a x w = 127 u \gt TMax_w = 127 u>TMaxw=127 时,例如 128 ≤ u ≤ 255 128 \le u \le 255 128u255,则原始高位1变成负号,补码释义为负数。原来位向量按无符号解释出的真值需减去 2 8 = 256 2^8=256 28=256,换算出按补码解释的正确真值,对应负数区间 [ − 1 , − 128 ] [-1, -128] [1,128]

我们在数轴上把有符号数和无符号数画出来的话,就能很清晰的看出相对的关系:
有符号&无符号

参考

《深入理解计算机系统》(第3版)
《计算机组成原理》(第3版),唐朔飞
《微机原理与接口技术》(第2版),王克义
《汇编语言程序设计》(第4版),文全刚

深入谈谈二进制
补码表示法的本质原理
【读薄 CSAPP】壹 数据表示
从晶体管开始聊聊计算机为什么采用二进制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/353027.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于STM32L431+Liteos的串口空闲中断加DMA循环接收

①MCU为STM32L431&#xff0c;使用串口2。 ②Liteos采用接管中断的方式。 STM32CubeMX配置生成串口代码&#xff1a; 串口DMA接收和发送配置区别是接收采用循环模式&#xff0c;发送为正常模式。 将生成的代码移植到liteos工程中&#xff0c;由于使用的接管中断的方式&#…

appium桌面版本以及一些自动化测试方方封装

标签&#xff08;空格分隔&#xff09;&#xff1a; appium_desktop 一 appium_desktop_v1.2.6 1.appium_desktop在github上最新下载地址&#xff1a;appium桌面版本地址 2.一路傻瓜式安装就好了&#xff1a; 3.然后点击搜索按钮&#xff08;右上角&#xff09; 三 inspector …

基于国产龙芯 CPU 的气井工业网关研究与设计(二)

3.1 系统硬件的总体设计 从硬件架构上&#xff0c;该 RTU 主要包括三大部分的设计&#xff1a; &#xff08;1&#xff09;外围电路设计&#xff1a;电源电路设计、RTC 电路设计和 EEPROM 电路设计。 &#xff08;2&#xff09;RTU 本体 I/O 端口设计&#xff1a;A/I 模拟量输入…

使用RedisDesktopManager无法连接Redis服务器问题

问题&#xff1a;解决办法问题1&#xff1a;redis的配置文件问题进入redis的目录 [rootredis ~]# cd /opt/apps/redis/ opt apps 是自己创建的文件夹&#xff08;用于安装redis&#xff09; 使用vim进入配置文件的修改 [rootredis redis]# vim redis.conf使用vim编辑器修改bi…

ERROR 1114 (HY000): The table ‘tt2‘ is full

insert 操作时提示is full 问题原因 rootlocalhost 11:55:41 [t]>show table status from t like ‘tt2’ \G ; *************************** 1. row *************************** Name: tt2 Engine: MEMORY Version: 10 Row_format: Fixed Rows: 7056 Avg_row_length: 944…

ASEMI代理FGH60N60SMD安森美ON原装原厂IGBT

编辑-Z 安森美FGH60N60SMD原厂IGBT参数&#xff1a; 型号&#xff1a;FGH60N60SMD 集电极到发射极电压&#xff08;VCES&#xff09;&#xff1a;600V 栅极到发射极电压&#xff08;VGES&#xff09;&#xff1a;20V 收集器电流&#xff08;IC&#xff09;&#xff1a;120…

纯css实现坤坤经典动作-“铁山靠”

背景 2023年2月16日&#xff0c;晴&#xff0c;今天没有工作&#xff0c;一直在掘金摸鱼&#xff0c;摸的我好累。 不行&#xff01;我得找点有意义的事情做&#xff01; 此时间&#xff0c;我发的一条沸点竟然有小黑子给我评论&#xff0c;\ 我看到之后气不打一处来&#xff…

1v1游戏互动,接入社交场景(内含接入方法)

游戏的需求洞察 许多互联网产品想要拓宽功能领域&#xff0c;纷纷选择进入小游戏赛道&#xff0c;试图从新颖有趣的方向深度触达用户&#xff0c;提高产品的活跃度和留存。群玩洞察了这一需求&#xff0c;还发现海外用户也对1v1互动游戏感兴趣&#xff0c;于是开始做这类游戏的…

中国人寿业务稳定性保障:“1+1+N” 落地生产全链路压测

引言 保险业务的数字化转型正如火如荼地进行&#xff0c;产品线上化、投保线上化、承保线上化、核保线上化等业务转型&#xff0c;导致系统的应用范围不断扩大&#xff0c;用户的高频访问也正在成为常态。同时&#xff0c;系统复杂性也呈指数上升&#xff0c;这些因素都增加了…

3.InfluxDB WEB使用

结合telegraf做指标数据收集 点击 Load Data -> Telegraf 配置界面 influxDB支持在WEB-UI中生成配置文件 然后利用telegraf通过远程URL请求的方式进行获取 点击CREATE CONFIGURATION 创建telegraf配置文件 选择Bucket InfluxDB提供了很多配置好的监控模板供用户选择 可以…

分享112个HTML旅游交通模板,总有一款适合您

分享112个HTML旅游交通模板&#xff0c;总有一款适合您 112个HTML旅游交通模板下载链接&#xff1a;https://pan.baidu.com/s/15OctJIB4NtyFddyalXX70A?pwd7tpa 提取码&#xff1a;7tpa Python采集代码下载链接&#xff1a;采集代码.zip - 蓝奏云 响应式旅游度假HTML5模板…

学会开发自己的Python AI应用【OpenAI API篇】

最近 OpenAI 宣布 ChatGPT 将很快推出他们的 API。虽然我们不知道这需要多长时间&#xff0c;但这之前我们可以熟悉下OpenAI API&#xff0c;快速开发自己的AI应用&#xff01;通过今天学习 OpenAI API&#xff0c;你将能够访问 OpenAI 的强大模型&#xff0c;例如用于自然语言…

动态生成复选框列表弹性盒子

效果图 1.准备一块盒子区域 id“checkBoxList” 为例 //样式的简单介绍 style{ overflow:scroll; //让超出div 内容在div区域内以滚动条形式呈现 display: flex; //搞一个弹性盒子容器 flex-wrap: wrap;align-content: flex-start; // 文本对齐方式 这里是上下元素的内容对齐…

【论文Word排版】使用多级列表设置论文序号

在Word中对论文进行排版 1.设置章节前面的序号 1.1 需求 通常情况下要求如下 一级标题“第一章 XXX”&#xff0c;然后是“1.1 研究意义”&#xff0c; “1.2 研究现状” 之前的处理方式都是手打&#xff0c;并没有借助word的多级列表实现。这次趁着写毕业论文研究了一下。…

虹科案例 | Redis企业版数据库帮助金融机构满足客户需求

传统银行无法提供无缝的全渠道客户体验、无法实时检测欺诈、无法获得业务洞察力、用户体验感较差、品牌声誉受损和业务损失&#xff1f;虹科提供的Redis企业版具有低延迟、高吞吐和高可用性特征&#xff0c;使用Redis企业版数据库&#xff0c;金融机构可以实现即时的客户体验、…

搭建Hexo博客-第3章-Markdown语言介绍及编辑博客

搭建Hexo博客-第3章-Markdown语言介绍及编辑博客 搭建Hexo博客-第3章-Markdown语言介绍及编辑博客 搭建Hexo博客-第3章-Markdown语言介绍及编辑博客 大家好&#xff0c;如果你按照上一篇文章的内容安装并部署了博客&#xff0c;那么现在在你的主页上应该有一篇 Hello World&…

工业上为什么要使用Io-Link?

工业上为什么要使用Io-Link&#xff1f;IO-Link是一种通讯技术&#xff0c;可以把传统的硬件从单纯的输入输出转变为可配置、可编程的网络设备。它可以大大减少有线连接&#xff0c;改善机器人控制和过程控制系统的可读性和可维护性&#xff0c;实现智能化的装置。使用IO-Link可…

科技云报道:2023,云计算的风向变了

科技云报道原创。 2022&#xff0c;是云计算的“分水岭”之年。 与前两年的火热相比&#xff0c;2022年云计算行业实属不太好过&#xff1a;阿里云一季度营收增速创出历史新低&#xff0c;腾讯云的市场份额也被后来者华为云反超&#xff0c;沦为第三。 在此情形下&#xff0c…

rabbitmq菜鸟教程,搭建rabbitmq

一、前言RabbitMQ是一个开源的遵循 AMQP协议实现的基于 Erlang语言编写&#xff0c;即需要先安装部署Erlang环境再安装RabbitMQ环境。需加注意的是&#xff0c;读者若不想跟着我的版本号下载安装&#xff0c;可根据两者版本号的对应表&#xff08;下面图示只展示了部分&#xf…

Git(GitHub,Gitee 码云,GitLab)详细讲解

目录第一章 Git 概述1.1 何为版本控制1.2 为什么需要版本控制1.3 版本控制工具1.4 Git 简史1.5 Git 工作机制1.6 Git 和代码托管中心第二章 Git 安装第三章 Git 常用命令3.1 设置用户签名3.2 初始化本地库3.3 查看本地库状态3.3.1 首次查看&#xff08;工作区没有任何文件&…