没有符号的数,每一位均可用来存放数值。
符号数字化的数称为机器数,而带正负号的数称为真值。
0表示正号,1表示负号。
符号位为0表示正数,符号位为1表示负数。又被称为带符号的绝对值表示
。
整数原码的定义为:
小数原码的定义为:
原码的0有两种表示形式。
(1)补数的概念
首先考虑数轴上正负数的几何意义,显然正整数n以看做从某一点(原点)向前走n步,而负整数n可以看做向后走n步,现在考虑下面的8进制圆盘(一个有限的数轴首尾相接),从1走到3,显然有两种走法,+2(顺时针两步)或-6(逆时针6步),可见,+2和-6在这个数轴上的意义是等效的,则称+6为-2的模8的补数。
利用补数,我们就将负数转化到了正数,这样只需利用加法器便能实现加减运算,简化了处理器的逻辑设计。
结论:
(2)补码的定义
整数补码的定义为:
小数补码的定义为:
补码的0只有一种表示形式。
-1本不属于小数范围,但却有-1的补码存在,其在原码中是不存在的。
当模数=4时,形成了双符号位补码,又称变形补码,在阶码运算和溢出判断中有特殊作用。
补码与原码相互转化:
对于四位二进制负整数x=x1x2x3x4
,考察其求补码的过程:
[
x
]
补
=
2
5
+
x
=
100000
+
x
=
11111
+
00001
−
x
1
x
2
x
3
x
4
=
1
x
1
−
x
2
−
x
3
−
x
4
−
+
00001
[x]_补 =2^5+x=100000+x=11111+00001-x_1x_2x_3x_4=1\overset{-}{x_1}\overset{-}{x_2}\overset{-}{x_3}\overset{-}{x_4}+00001
[x]补=25+x=100000+x=11111+00001−x1x2x3x4=1x1−x2−x3−x4−+00001
这就是我们熟知的由原码求补码的过程:符号位不变,数值位按位取反,末尾+1。
这一规则同样适用于已知X补
求X原
。
整数反码的定义为:
小数反码的定义为:
反码的0也有2种表示形式。
由[x]_补
求[-x]_补
:连同符号位在内,每位取反,末位+1。
引入移码的目的:为了能从机器数直观地看出真值的相对大小。
移码常用于表示浮点数的阶码。
移码的定义为:
移码中0的表示也是唯一的。
小数没有移码。
最小真值的移码全为0。
补码和移码的相互转化:符号位取反。
定点数是指小数点以约定的位置给出,小数点位置在最前面的称为小数定点机,小数点位置在最后面的称为整数定点机。
浮点数:小数点位置可以浮动的数。
浮点数表示成:
N
=
S
×
r
j
N=S{\times}r^j
N=S×rj
S为尾数,j为阶码,r为基值(2,4,8,16……)。
规定尾数用纯小数表示,同时尾数最高位=1的浮点数称为规格化数(精度最高)。
r越大,可表示的浮点数范围越大,而且能表示的数的个数越多,但精度下降。
判为机器0的条件:
当阶码用移码表示,尾数用补码表示,则机器零=“000…0”,有利于机器判0电路的实现。
下面给出一个查看浮点数二进制表示的工具(没有经过完整测试):
#include <stdio.h> #include <stdlib.h> #include <string.h> void showFloatBin(float f); void showDoubleBin(double d); int main(int argc, char* argv[]) { if (argc != 3) { printf("input error\n"); return 0; } if (!strcmp(argv[1], "f")) { float f = atof(argv[2]); showFloatBin(f); } else if (!strcmp(argv[1], "d")) { double d = atof(argv[2]); showDoubleBin(d); } else { printf("input error\n"); } return 0; } void showFloatBin(float f) { unsigned int mask = 0x80000000; unsigned int f_puppet; memcpy(&f_puppet, &f, 4); for (int i = 0; i < 32; ++i) { if (f_puppet & mask) { printf("1"); } else { printf("0"); } if (i == 0 || i == 8) printf(" "); f_puppet <<= 1; } printf("\n"); } void showDoubleBin(double d) { unsigned long long mask = 0x8000000000000000; unsigned long long d_puppet; memcpy(&d_puppet, &d, 8); for (int i = 0; i < 64; ++i) { if (d_puppet & mask) { printf("1"); } else { printf("0"); } d_puppet <<= 1; if (i == 0 || i == 11) printf(" "); } printf("\n"); }
程序效果:
daniel@vostro:~/Project/temp$ ./showBin f -178.125 1 10000110 01100100010000000000000 daniel@vostro:~/Project/temp$ ./showBin f 178.125 0 10000110 01100100010000000000000
硬件实现:
为了避免算术左移时最高数位丢1,可以采用带进位Cy的移位:
加法:
减法:
符号位和数值部分一起参与运算,并且将符号位产生的进位自然丢掉即可。
(1)用一位符号位判断溢出
overflow
=符号位产生的进位
^最高有效位产生的进位
(2)用两位符号位判断溢出
第一位的符号永远代表真正的符号。
运算过程:
乘积的符号位由两个操作数的符号位异或得到。
(1)原码一位乘运算规则
看着挺抽象,做道题就懂了:
结果是0.1011_0110
(2)原码一位乘所需的硬件配置
(3)原码一位乘控制流程
(4)原码两位乘
用两位乘数的状态来决定新的部分积如何形成,因此可以提高运算速度。
(了解即可,应该不会考)
(1)补码一位乘运算规则
被乘数x符号任意,乘数y符号为正
[
x
⋅
y
]
补
=
[
x
]
补
⋅
[
y
]
补
=
[
x
]
补
⋅
y
[x·y]_补=[x]_补·[y]_补=[x]_补·y
[x⋅y]补=[x]补⋅[y]补=[x]补⋅y
当乘数y为正数,不管被乘数x的符号如何,都可按原码乘法的规则运算。这里的加和移位必须按补码的规则计算。
被乘数x符号任意,乘数y符号为负
[
x
⋅
y
]
补
=
[
x
]
补
(
0.
y
1
y
2
.
.
.
y
n
)
+
[
−
x
]
补
[x·y]_补=[x]_补(0.y_1y_2...y_n)+[-x]_补
[x⋅y]补=[x]补(0.y1y2...yn)+[−x]补
把乘数的补码y补
去掉符号位,当成一个正数与x相乘,然后加上[-x]补
进行校正,也称校正法。
被乘数x和乘数y符号均任意(Booth算法)
符号位参与运算,运算的数均以补码表示;
被乘数和部分积取双符号位,初值=0,乘数可取单符号位。
乘数末尾增设附加位yn+1=0;
根据(yn,yn+1)的取值来确定操作:
移位按照补码右移规则进行;
按照上述算法进行n+1步,但最后一步不再移位。
一个例子:
(2)补码比较法(Booth算法)所需的硬件配置
(3)补码比较法(Booth算法)控制流程
(4)补码两位乘
提高运算速度,了解即可。
商符由两个符号位异或求得,商值由两数绝对值相除得。
在小数定点机中,规定0<被除数<=除数
,否则产生溢出。
(1)恢复余数法
当余数为负时,需要加上除数,将其恢复为原来的余数。
(2)加减交替法(不恢复余数法)
举个例子便知:
(3)原码加减交替法所需的硬件配置
(4)原码加减交替法控制流程
(1)补码加减交替法运算规则
比较被除数和(余数)和除数的大小:
商值的确定:
商符是在求商值的过程中自动形成的。
商的符号还可以判断商是否溢出。
新余数的确定:
举个例子:
(2)补码加减交替法所需的硬件配置
与原码的配置相同,但是触发器S可以省略,因为符号位在运算过程中确定。
(3)补码加减交替法的控制流程
步骤:对阶-尾数求和-规格化-舍入-溢出判断。
小阶向大阶看齐:阶数小的尾数向右移位,每右移一位,阶码+1。
尾数右移时可能会发生数码丢失,影响精度。
尾数的最高数值位与符号位不同时,即为规格化形式。对于S<0,有两种特殊情况:
(1)左规
(2)右规
尾数结果出现01.xxx...x
或10.xxx...x
时,并不表示溢出,只有将此数右归后,根据阶码才可以判断运算结果是否溢出。
浮点数的溢出与否由阶码的符号决定。
略,大纲不要求。
由两个定点运算部件组成:
还需要有判断结果是否溢出的电路等。
C-1表示最低位的外来进位,Cn+4是向高位的进位。P,G供先行进位使用。
并行加法器中的进位信号采用串行传递。
并行加法器中的进位信号是同时产生的,又称先行进位,跳跃进位等。
(1)单重分组跳跃进位
将n位全加器分为若干小组,小组内的进位同时产生,小组与小组之间采用串行进位,这种进位又有组内并行,组间串行之称。
(2)双重分组跳跃进位