在数字逻辑系统中,只存在高电平和低电平,因此用其表示数字只有整数形式,并存在3种表示方法,即:原码表示法(符号加绝对值)、反码表示法(符号加反码)和补码表示法(符号加补码)。这三种在FPGA开发中都有着广泛的应用,下面分别讨论。
1、原码表示法
原码表示法是机器数的一种简单的表示法,采用符号位级联绝对值的方法表示数字。其最高位为符号位,用0表示正数,1表示负数;其余部分为绝对数值部分。原码一般用二进制形式表示。
例如,X1 = +1010110,X2 = -1001010,则其原码分别为:01010110和11001010
原码表示数的范围与二进制位数有关。当用8位二进制来表示小数原码时,其表示范围:最大值为0.1111111,其真值约为10进制中的0.99;最小值为1.1111111,其真值约为十进制的-0.99。当用8位二进制来表示整数原码时,其表示范围:最大值为01111111,其真值为十进制的127;最小值为11111111,其真值为十进制的-127。
在原码表示法中,对0有两种表示形式,分别记为+0和-0,以8比特数据为例,其相应的表示为:+0=00000000、-0=10000000。
2、反码表示法
反码可由原码得到。如果数字是正数,则其反码与原码一样;如果数字是负数,则其反码是对它的原码(符号位除外)各位取反而得到的。
例如:X1 = +1010110, X2=-1001010,则其相应的反码为01010110、10110101。
3、补码表示法
补码表示法师实际中应用最广泛的数字表示法,其表示规则如下:若是正数,补码、反码和原码的表示是一样的;若是负数,补码、反码和原码的表示都不一样。
由反码与原码之间的关键,负数的补码等于其反码在最低位加1。
4、各类表示方法小结
原码的优点就是乘除运算方便,不论正负数,乘除运算都一样,并以符号位决定结果的正负号;若做加法则需要判断两数符号是否相同;若作减法,还需要判断两数绝对值的大小,以使大数减小数。
补码的优点是,加法运算方便,不论数的正负都可直接相加,而符号位同样参加运算。
例:给出各类码字表示法的基本加法运算实例,并说明各自特点
(1)首先给出原码的运算示例,其中()d代表十进制数。首先给出一个原码的减法计算实例,完成“1 + (-1)= 0“”的操作。
(1)d + (-1)d = (0)d
如果读者直接利用原码来完成上式运算,会发现用符号位的原码进行在加减运算的时候就会出现问题,将数据以8比特的表示形式为例,
(00000001)原 + (10000001)原 = (10000001)原 = (-2)d
计算结果是不对的,问题在于两点:首先,负数的符号位直接改变了计算结果符号;其次,绝对值部分计算也不正确。这说明原码无法直接完成正数和负数的加法。
(2)既然,原码不能完成正、负数相加,那么反码形式可以完成此操作吗?仍然以”1 + (-1) = 0“ 为例,其相应的反码表达式如下:
(00000000)反 + (11111110)反 = (11111111)反 = (-0)d
则发现问题出现在+0和-0上,因为实际的计算中的零没有正负之分的。但上式标明了反码完成正、负数相加后,其绝对值部分是正确的,因此能正确完成正、负数相加的表达形式必定包含反码的特性。
(3)最后给出补码的相关特性说明,负数的补码就是对反码加1,而正数不变。以8比特数据为例,通过(-128)d代替了(-10)d,所以其表示范围为【-128,127】。从直观上,补码消除了(+0)和(-0),并且具备反码特点,那么究竟其能完成正、负加法运算吗?答案是肯定的,下面给出具体实例,所示
(00000001)补 + (11111111)补 = (00000000)补 = (0)d
基于以上讨论,可以得到一个基本结论:只有补码才能正确完成正负加法运算,并将减法运算转化为加法运算,从而简化运算规则。
但对于乘法操作,则以原码形式计算是最方便的,下面有实例。
演示1:原码进行乘法运算 -2 * 2 = -4
module mul(
input clk,
input rstn,
input [7:0] a,
input [7:0] b,
output [14:0] q_mul,
output reg [8:0] q_add,
output reg[7:0] ra,
output reg[7:0] rb
);
reg [13:0] rmul;
always @(posedge clk or negedge rstn)
if(!rstn)
begin
q_add <= 0;
ra <= 0;
rb <= 0;
rmul <= 0;
end
else begin
//q_mul <= a*b;
if(a[7]==1)
ra = {a[7],~a[6:0] + 1};
else
ra = a;
if(b[7]==1)
rb = {b[7],~b[6:0] + 1};
else
rb = b;
rmul <= ra[6:0]*rb[6:0];
q_add <= {a[7],a} + {b[7],b};
end
assign q_mul = {a[7]^b[7],rmul};
endmodule
演示2:通过reg signed实现
module signedMul(
input clk,
input rstn,
input [7:0] a,
input [7:0] b,
output [15:0] q
);
reg signed[7:0] ra;
reg signed[7:0] rb;
always @(posedge clk or negedge rstn) begin
if(~rstn) begin
ra <= 0;
rb <= 0;
end
else begin
ra <= a;
rb <= b;
end
end
assign q = ra * rb;
endmodule