楼主有话要说:
在复习《计算机组成原理》时,遇到了有符号数与无符号数之间转换的问题,为了应对这类问题,楼主仔细查阅资料再结合做题总结出一些心得,内容如下(如果有新的总结,会反复修改):
注意:
1、字长都用的是 16 位(方便些)
2、建议先去大致了解下 C++ 里基本数据类型的字节长度和示数范围;如:
char | 1 byte | int | 4 byte | short [int] | 2 byte | float | 4 byte | long [int] | 4 byte |
signed char | 1 byte | signed int | 4 byte | signed short [int] | 2 byte | double | 8 byte | signed long [int] | 4 byte |
unsigned char | 1 byte | unsigned int | 4 byte | unsigned short [int] | 2 byte | long double | 8 byte | unsigned long [int] | 4 byte |
3、(1)定点整数的范围( 16 bit ) :有符号:-32767 <= x <= 32767
(2)定点整数补码的范围( 16 bit ) :有符号:-32768 <= x <= 32767
4、在计算机中,负数一般用补码表示。注意:本文所有例子,都是先将要赋值的数化为补码的形式再进行转换。
5、理清 0 和 1 的原码、反码、补码。(0 的补码和移码是只有一种形式;正数的原码、反码、补码都相同;)
原码 | 反码 | 补码 | |
[ +0 ] | 0000 0000 | 0000 0000 | 0000 0000 |
[ -0 ] | 1000 0000 | 1111 1111 | 0000 0000 |
[ +1 ] | 0000 0001 | 0000 0001 | 0000 0001 |
[ -1 ] | 1000 0001 | 1111 1110 | 1111 1111 |
一、同类型(如同为有符号数,或同为无符号数)转换
(一)1. 同为无符号数(由短变长:在最高位加 “ 0 ”。)
unsigned short x = 255; //short为2字节
unsigned int y = x; //int为4字节
x = 255 D = 1111 1111 B
y = 0000 0000 1111 1111 B = 255 D
总结:同为无符号数时,由较短的数变成较长的数,即在最高位加 “ 0 ”,称为 “ 零扩展 ”。
(一)2. 同为无符号数(由长变短:在最高位加 “ 0 ”。)
unsigned int x = 255; //short为2字节
unsigned short y = x; //int为4字节
x = 255 D = 0000 0000 1111 1111 B
y = 1111 1111 B = 255 D
总结:同为无符号数时,由较长的数变成较短的数,即去掉高位的 “ 0 ” 字节,低位的字节不变。
(二)同为有符号数(由短变长和由长变短的情况同 “(一)同为无符号数 ” 一样)
short x = -120; //short为2字节
int y = x; //int为4字节
x = -120 D ,其原码为:1111 1000 B,其补码为:1000 1000 B (= 88 H)
y = 1111 1111 1000 1000 B = -120 D (= FF 88 H)
总结:同为有符号数时,使用符号扩展,即为负数时,高位的字节加 “ 1 ”;为正数时,高位的字节加 “ 0 ”。
二、有符号数与无符号数之间的转换
(一)有符号数转为无符号数
short n = -1;
unsigned short m = n;
n = -1 D,其补码为:1111 1111 B
m = 1111 1111 B = 65535 D
注释:n 的最高位为 “ 1 ” 表示的是符号位,因为 m 为无符号数,所以 m 的最高位为 “ 1 ” 表示的是“ 2的7次方 ”。
总结:有符号数转为无符号数时,当有符号数为负数时,关键是将最高位的 “ 1 ” 变成 “ 2的某次方 ”。
(二)无符号数转为有符号数
unsigned short a = 65535;
short b = -1;
a = 65535 = FF FF FF FF H
b 的补码即为:FF FF FF FF H = -1 D
注释:因为 b 为有符号数,最高位可知为 “ 1 ”,所以 b 为负数,可知其值为 -1。
总结:无符号数转为有符号数时,先得出赋值数的补码,即为被赋值数的补码;再由被赋值数的补码取反加一得到原码,再计算即得十进制的数值。
再举个例子:
unsigned char i = 130;
char k = i;
i = 130 D = 1000 0010 B (注意:正数的原码 = 正数的补码 = 正数的反码)
k 的补码为:1000 0010 B,进行取反加一后得原码为:1111 1110 B,因为 k 为有符号数,最高位又为 “ 1 ”,所以 k 为负数,即为 -126 D。
练习:(附上2011年统考真题,便于理解巩固)
假定在一个8位字长的计算机中运行如下C程序段:
unsigned int x = 134;
unsigned int y = 246;
int m = x;
int n = y;
unsigned int z1 = x - y;
unsigned int z2 = x + y;
int k1 = m - n;
int k2 = m + n;
若编译器编译时将 8 个 8 位寄存器 R1 - R8 分别分配给变量x、y、m、n、z1、z2、k1、k2,请回答下列问题(提示:带符号整数用补码表示)
( 1 )执行上述程序段后,寄存器 R1、 R5 和 R6 的内容分别是什么?(用十六进制表示)
( 2 )执行上述程序段后,变量 m 和 k1 的值分别是多少?(用十进制表示)
( 3 )上述程序段涉及带符号整数加/减、无符号整数加/减运算,这四种运算能否利用同一个加法器辅助电路实现?简述理由。
( 4 )计算机内部如何判断带符号整数加/减运算的结果是否发生溢出?上述程序段中,哪些带符号整数运算语句的执行结果会发生溢出?
解:( 1 )寄存器 R1、 R5 和 R6 的内容,分别对应 x, z1(x - y) , z2(x + y)。
x = 134 D,其原码等于补码为 1000 0110 B,即为 86 H,即为 R1
y = 246 D,其原码等于补码为 1111 0110 B,即为 F6 H
z1 = x - y = x 的补码 + [-y] 的补码 = 1000 0110 B + 0000 1010 B = 1001 0000 B = 90 H,即为 R5
z2 = x + y = x 的补码 + y 的补码 = 1000 0110 B + 1111 0110 B = (1) 0111 1100 B = 7C H,即为 R6
(注意:z2 处有溢出,但无符号整数的加减运算,一般不考虑溢出情况,只是输出的时候若是有符号数的最高位是符号位,意思是 z2 若转换为有符号数,最高位为 1。)
所以 R1 = 134 = 86 H;
R5 = 90 H;
R6 = 7C H;
( 2 )m 为无符号数整型 x 转为有符号数整型,n 为无符号数整型 y 转为有符号数整型,由(1)知
x 的补码为 1000 0110 B,y 的补码为 1111 0110 B,所以
m 的补码为 1000 0110 B,其原码为1111 1010 B,其值为 -122 D
n 的补码为 1111 0110 B,其原码为 1000 1010 B,其值为 -10 D
即 k1 = m - n = m 的补码 + -n 的补码 = 1000 0110 B + 0000 1010 B = 1001 0000 B = 90 H,求得其原码为 1111 0000 B = -112 D
顺带求下 k2 = m + n = m 的补码 + n 的补码 = 1000 0110 B + 1111 0110 B = (1) 0111 1100 B = 7C H(注意:此处有溢出)
所以,m = -122;
k1 = -112;
题外话(对 OF 、SF、CF 也要注意区分):
k2 = m + n,由已知 m = -122,n = -10,可知两者相加仍为负数,但结果为正。所以,溢出标志位 OF = 1, 表示溢出,说明寄存器 R6 中的内容不是真正的结果;符号标志位 SF = 0,表示结果为正数(溢出标志为1,说明符号标志有错),进位标志位 CF = 1,仅表示加法器最高有进位,对运算结果不说明什么。
( 3 ) 能。n 位加法器实现的是模 2 的 n 次方无符号整数加法运算。因为在计算机中,正数和负数均用补码来表示,带符号整数的加法运算和无符号整数的加法运算,均可直接用加法器实现。对于减法运算,只要减数的补码加上被减数的负数的补码即可实现,但要考虑溢出情况。所以带符号整数加/减、无符号整数加/减运算均可用同一个加法器辅助电路实现(溢出判断电路不同)。
( 4 )是。计算机内部判断带符号整数加/减运算的结果是否发生溢出,有 3 种方法。一是若加法器的两个输入端(加法)的符号相同,且不同于输出端(和)的符号,则结果溢出。二是加法器完成加法操作时,若次高位(最高数位)的进位与最高位(符号位)的进位不同,则结果溢出。三是两个带符号整数均为负数,两者相加后,结果小于 8 位二进制所能表示的最小负数,则结果溢出。
在执行关于 k2 的语句时会发生溢出。k2 = 1000 0110 B + 1111 0110 B = (1) 0111 1100 B,括号中为加法器的进位,因为 2 个带符号整数均为负数,经过加法运算后得出结果的最高位为 0 ,所以溢出。
(溢出判断方法简单版:双符号位判决法、最高位进位、符号相同操作数的运算后与原操作数的符号不同,则结果溢出。)
注释:
双符号位判决法:
用补码进行双符号位运算(正数符为 00, 负数符号为 11),第一符号位表示最终结果的符号,第二符号位表示运算结果是否溢出。第一符号位和第二符号位相同,则未溢出,;若不同,则溢出。具体如下:
若运算结果的符号位为 01, 则为正溢出;
若结果双符号为 10, 则为负溢出;
若结果的双符号位为 00,结果为正数,无溢出。
若结果的双符号位为 11,结果为负数,无溢出。