更新历史
20190324:
- 首次发布
使用定点小数,可以将小数的存储和运算,转化为整数的存储和运算。由于定点小数通常在硬件计算中使用,故而,我们尤其关心的是二进制下的定点小数运算。
接下来,本文将从十进制下非负定点小数的乘法、加法、除法讲起,让读者对定点小数运算上的一些注意事项有所了解,然后讲二进制下无符号和有符号定点小数的乘法、加法、除法。
十进制下的两个小数:
如果我们计算A、B的乘积,得到的结果是:
A × B = 999.9 × 9.99 = 9989.001 A \times B = 999.9 \times 9.99 = 9989.001 A×B=999.9×9.99=9989.001
对于乘法运算得到的结果"9989.001",我们可以看到,其整数部分有4位,其小数部分有3位。我们并不会对其感到奇怪,因为:
好了,现在我们使用定点小数来替代原先的小数,看看情况变成什么样了:
由于我们事先就知道 A 定 A_定 A定 的整数部分有3位,小数部分有1位, B 定 B_定 B定 整数部分有1位,小数部分有2位,所以,对于乘法运算得到的整数"9989001",我们知道:
对于上面的分析,我们做一下归纳总结,可以得到:
设十进制下的两个非负的定点小数:
A定 = M位整数部分 + X位小数部分
B定 = N位整数部分 + Y位小数部分
那么:
(A定 × B定) = (M+N)位整数部分 + (X+Y)位小数部分
注:运算结果不会产生溢出
十进制下的两个小数:
如果我们计算A、B的和,得到的结果是:
A + B = 999.9 + 9.99 = 1009.89 A + B = 999.9 + 9.99 = 1009.89 A+B=999.9+9.99=1009.89
现在,我们使用定点小数来替代原先的小数,看看情况变成什么样了:
看到这里,机智的你一定发现情况不对了吧?定点小数"10998"对应的实际小数怎么看都不会是"1009.89",是吧?那么,问题出在哪里呢?我们可以看看运算过程中的数据位的对应情况:
计算"A + B": 计算"A定+B定":
9 9 9 . 9 9 9 9 9
+ 9 . 9 9 + 9 9 9
---------------------------- -------------------
1 0 0 9 . 8 9 1 0 9 9 8
嗯,看来我们找到问题的真相了:
所以,我们应该改成:
计算"A定+B定":
9 9 9 9 (0)
+ (0) (0) 9 9 9
----------------------------
1 0 0 9 8 9
定点小数"100989"对应的实际小数正是"1009.89"。
对于上面的分析,我们做一下归纳总结,可以得到:
设十进制下的两个非负的定点小数:
A定 = M位整数部分 + X位小数部分
B定 = N位整数部分 + Y位小数部分
那么:
计算(A定 + B定)之前,要采取相应的补0措施,使得两个定点小数的整数部分位宽一致,小数部分的位宽一致。
(A定_补0 + B定_补0) = max(M,N) 位整数部分 + max(X,Y)位小数部分
注:运算结果可能会产生进位溢出
十进制下的两个小数:
如果我们计算A、B的商,得到的结果是:
A ÷ B = 999.9 ÷ 9.99 = 100.09 0 ˙ 0 ˙ 9 ˙ ≈ 100 A \div B = 999.9 \div 9.99 =100.09\dot0\dot0\dot9 \approx 100 A÷B=999.9÷9.99=100.090˙0˙9˙≈100
现在我们使用定点小数来替代原先的小数,看看情况变成什么样了:
咦,又出问题了?意外不?惊喜不?咳,言归正传,这里出现问题的原因是:
所以,要想得到正确的商,在做除法之前,需要先在 A 定 A_定 A定的低位补0(或者说,将 A 定 A_定 A定乘上10)。
对于上面的分析,我们做一下归纳总结,可以得到:
设十进制下的两个非负的定点小数:
A定 = M位整数部分 + X位小数部分
B定 = N位整数部分 + Y位小数部分
那么:
计算(A定 ÷ B定)之前,要采取相应的补0措施,使得两个定点小数的小数部分的位宽一致,且A的整数部分位宽不小于B的整数部分位宽。
(A定_补0 ÷ B定_补0) = ( max(M,N) + max(X,Y) ) bit整数部分
到此为止,通过对十进制下定点小数运算的分析,我们已经接触到了定点小数运算过程中的一些“坑”了。下面,让我们小心地避开这些“坑”,来分析二进制下有符号和无符号定点小数的乘法、加法、除法。
对于二进制下无符号定点小数的乘法运算,其运算结果的位宽变化规律和十进制下非负定点小数乘法相同:
设二进制下的两个无符号定点小数:
A定 = M bit整数部分 + X bit小数部分
B定 = N bit整数部分 + Y bit小数部分
那么:
(A定 × B定) = (M+N) bit整数部分 + (X+Y) bit小数部分
注:运算结果不会产生溢出
而对于二进制下有符号定点小数的乘法运算,由于牵涉到符号位,其运算结果的位宽变化规律稍稍有些不同,分析可知:
设二进制下的两个有符号定点小数:
A定 = 1 bit符号位 + M bit整数部分 + X bit小数部分
B定 = 1 bit符号位 + N bit整数部分 + Y bit小数部分
那么:
(A定 × B定) = 1 bit符号位 + (M+N) bit整数部分 + (X+Y) bit小数部分
注:运算结果不会产生溢出
可以举例说明:
对于二进制下无符号定点小数的加法运算,其运算结果的位宽变化规律和十进制下非负定点小数加法相同:
设二进制下的两个无符号定点小数:
A定 = M bit整数部分 + X bit小数部分
B定 = N bit整数部分 + Y bit小数部分
那么:
计算(A定 + B定)之前,要采取相应的补0措施,使得两个定点小数的整数部分位宽一致,小数部分的位宽一致。
(A定_补0 + B定_补0) = max(M,N) bit整数部分 + max(X,Y) bit小数部分
注:运算结果可能会产生进位溢出
而对于二进制下有符号定点小数的加法运算,由于牵涉到符号位,其运算结果的位宽变化规律稍稍有些不同,分析可知:
设二进制下的两个有符号定点小数:
A定 = 1 bit符号位 + M bit整数部分 + X bit小数部分
B定 = 1 bit符号位 + N bit整数部分 + Y bit小数部分
那么:
计算(A定 + B定)之前,要采取相应的低位补0措施,使得两个定点小数的小数部分的位宽一致。
计算(A定 + B定)之前,要采取相应的次最高位补0(非负数)、次最高位补1(负数)措施,使得两个定点小数的整数部分的位宽一致。
(A定_补齐 + B定_补齐) = 1 bit符号位 + max(M,N) bit整数部分 + max(X,Y) bit小数部分
注:运算结果可能会产生进位溢出
可以举例说明:
对于二进制下无符号定点小数的除法运算,其运算结果的位宽变化规律和十进制下非负定点小数除法相同:
设二进制下的两个无符号定点小数:
A定 = M bit整数部分 + X bit小数部分
B定 = N bit整数部分 + Y bit小数部分
那么:
计算(A定 ÷ B定)之前,要采取相应的补0措施,使得两个定点小数的小数部分的位宽一致,且A的整数部分位宽不小于B的整数部分位宽。
(A定_补0 ÷ B定_补0) = ( max(M,N) + max(X,Y) ) bit整数部分
而对于二进制下有符号定点小数的除法运算,由于牵涉到符号位,其运算结果的位宽变化规律稍稍有些不同,分析可知:
设二进制下的两个有符号定点小数:
A定 = 1 bit符号位 + M bit整数部分 + X bit小数部分
B定 = 1 bit符号位 + N bit整数部分 + Y bit小数部分
那么:
计算(A定 ÷ B定)之前,要采取相应的低位补0措施,使得两个定点小数的小数部分的位宽一致。
计算(A定 ÷ B定)之前,要采取相应的次最高位补0(非负数)、次最高位补1(负数)措施,使得A的整数部分位宽不小于B的整数部分位宽。
(A定_补齐 ÷ B定_补齐) = 1 bit符号位 + ( max(M,N) + max(X,Y) ) bit整数部分
可以举例说明:
注:虽然这里讲了二进制的除法,然而实际上,由于除法实现起来电路比较复杂,因此往往能避免使用除法就尽量避免使用除法。非要做除法运算的话,可以考虑使用集成的除法单元,或者采用辗转相除法自己写逻辑实现。
在硬件计算中,减法运算的电路比较复杂,故而,通常将(a-b)运算转化为(a+(-b)),将减法运算转化为加法运算。而将b转化为(-b)是相对简单的。因此,定点小数的减法运算,就拜拜啦。。
用一句话将本论述归纳总结一下,那就是: