定点数的乘法及格式转换
文章目录
1.乘法运算
定点数乘法可以用传统的整数乘法硬件实现,输出数据位宽是参与相乘的两个数位宽之和,并且输出的小数位数是参与相乘的两个数的小数位数之和。
例如,S3.4的数据 -1.5 乘以 S5.2 数据 3.75,乘法结果是 S9.6 的数据 -5.625
对应的代码如下:
signed char a=-24;
signed char b= 15;
signed short result=((unsigned short)a)* ((unsigned short)b);
// 将计算结果转换为浮点数表示
double result_float=(double)result/(double)(1<<6);
2.格式转换
应用过程中会遇到定点数的格式转换,比如格式为 $ S_{{n_1}·{m_1}} $ 定点数转换为 $ S_{{n_2}·{m_2}} KaTeX parse error: Expected '}', got 'EOF' at end of input: ,需要根据 { n_1, m_1, n_2, m_2 $ }的大小关系分别处理
2.1 $ n_1 < n_2 $
$ S_{{n_1}·{m_1}}$ 对应的二进制数据左边额外添加 $ n_2 - n_1 位,里面填充 位,里面填充 位,里面填充 S_{{n_1}·{m_1}}$ 原先的最高位(正数填充0,负数填充1)
Example:
- $ S_{3.4} 定点数 ‘ 00111100 ‘ 转成 定点数 `0011 1100`转成 定点数‘00111100‘转成 S_{5.4}$定点数为 00 0011 1100
- $ S_{3.4} 定点数 ‘ 10101100 ‘ 转成 定点数 `1010 1100`转成 定点数‘10101100‘转成 S_{5.4}$定点数为 11 1010 1100
2.2 $ n_1 > n_2 $
$ S_{{n_1}·{m_1}}$ 对应的二进制数据的高 $ n_2 - n_1$位被解除
注意:这个操作可能发生溢出,这时,通常会执行饱和运算:
- $ S_{{n_1}·{m_1}} 是正数,将转换结果设置为 是正数,将转换结果设置为 是正数,将转换结果设置为 S_{{n_2}·{m_2}} $可以表示的最大正数;
- $ S_{{n_1}·{m_1}} 是负数,将转换结果设置为 是负数,将转换结果设置为 是负数,将转换结果设置为 S_{{n_2}·{m_2}} $可以表示的最小负数;
Example:
-
$ S_{5.4}$ 定点数
00 0011 1100
(3.75)转成$ S_{3.4}$定点数为0011 1100
(3.75) -
$ S_{5.4}$ 定点数
11 0011 1100
(-12.25)转成$ S_{3.4}$ 定点数,如果直接截取高位2位,则变成0011 1100
(3.75)。需要执行饱和处理,将定点数表示为$ S_{3.4}$能够表示的最小负数,即1000 0000
(-8.0)
2.3 $ m_1 < m_2 $
$ S_{{n_1}·{m_1}}$ 对应的二进制数据最右边额外添加 m 2 − m 1 m_2 - m_1 m2−m1个0
Example:
- $ S_{3.2}$ 定点数
0011 11
(3.75)转成$ S_{3.4}$ 定点数为0011 1100 - $ S_{3.2}$ 定点数
1011 11
(-4.25)转成$ S_{3.4}$ 定点数为 1011 1100
2.4 $ m_1 > m_2 $
$ S_{{n_1}·{m_1}}$ 对应的二进制数据最右边截去 m 1 − m 2 m_1 - m_2 m1−m2个0
为了提高精度,可以考虑对截去的位进行舍入运算
Example:
- $ S_{3.4}$ 定点数
0001 0101
(1.3125)转成$ S_{3.2}$ 定点数,截去最后2位得到0001 01
(1.25) - $ S_{3.4}$ 定点数
1110 1011
(-1.3125)转成$ S_{3.2}$ 定点数,如果直接截取高位2位,则变成1110 10
(-1.5)。如果需要考虑舍入,则需要检查截除的2位(11),其中高位为1,于是给直接截去的结果加 1,即1110 11
(-1.25),更接近原始值了。
定点数 a 减少 k 位小数位数的代码如下:
signed short b = (a + ( 1 << (k - 1))) >> k;
+ ( 1 << (k - 1)
实现了舍入运算 。
3.定点类及运算方法代码
# 定点数类
class fxp_c:
def __init__(self, v=0, n=0, m=15):
self.set_param(n,m) # 格式相关的参数设置
self.set(v) # 值设置
def set_param(self,n,m):
self.n=n # 整数位宽(不考虑符号位)
self.m=m # 小数位宽
self.k=m+n+1 # 总位宽
self.imax= 2**(m+n)-1 # 最大可能值
self.imin=-2**(m+n) # 最小可能值
self.scale=2**self.m # 小数对应的倍乘因子
# 修改小数位数
def change_m(self,m):
if m==self.m: return self.v
if m>self.m:
i=self.i*2**(m-self.m) # 左移
else:
i=self.i+2**(self.m-m-1)# 舍入
i//=2**(self.m-m) # 右移
self.set_param(self.n,m) # 设置新的格式参数
return self.seti(i)
# 修改整数位数
def change_n(self,n):
if n==self.n: return self.v
self.set_param(n,self.m) # 设置新的格式参数
return self.seti(self.i) # 考虑可能溢出,需要重新设置
# 以整数形式设置内容
def seti(self,i,sat=True):
self.i=min(self.imax,max(i,self.imin)) if sat else i # 饱和
if self.i != i: print('saturation in seti()')
self.v=float(self.i)/float(self.scale) # 计算值
return self
# 以bit串形式设置内容
def setb(self,s): return self.seti(bin_to_int(s,self.k))
# 设置实数值
def set(self,v): return self.seti(int(round(v*float(self.scale))))
# 以bit串形式返回内容
def to_bits(self,has_point=True):
s=int_to_bin(self.i,self.k)
return s[:self.n+1]+'.'+s[self.n+1:] if has_point else s
# 打印信息
def dump(self):
print('val:',self.v)
print('int:',self.i)
print('bit:',self.to_bits())
print(' m:',self.m)
print(' n:',self.n)
print(' k:',self.k)
def check(self):
if self.i> 2**(self.k-1)-1 : print('error')
if self.i<-2**(self.k-1) : print('error')
if self.k!=self.m+self.n+1 : print('error')
if self.v*(2.**self.m)-float(self.i) > 0.5*2.**(-self.m): print('error')
if self.scale!=2**self.m : print('error')
if self.imax!=2**(self.k-1)-1 : print('error')
if self.imin!=-2**(self.k-1) : print('error')
## 定点数复制
def fxp_copy(s): return fxp_c(s.v,s.n,s.m)
## 定点数格式转换
def fxp_conv(s,n,m):
s_new=fxp_copy(s)
s_new.change_n(n)
s_new.change_m(m)
return s_new
## 定点数相加
def fxp_add(s1,s2):
# 获取格式参数
n,m = max(s1.n,s2.n), max(s1.m,s2.m)
s1_new=fxp_conv(s1,n,m)
s2_new=fxp_conv(s2,n,m)
s1_new.seti(s1_new.i+s2_new.i)
return s1_new
## 定点数减
def fxp_sub(s1,s2):
# 获取格式参数
n,m = max(s1.n,s2.n), max(s1.m,s2.m)
s1_new=fxp_conv(s1,n,m)
s2_new=fxp_conv(s2,n,m)
s1_new.seti(s1_new.i-s2_new.i)
return s1_new
## 定点数乘
def fxp_mul(s1,s2):
# 获取格式参数
k = s1.k+s2.k
m = s1.m+s2.m
return fxp_c(s1.i*s2.i,k-m-1,m)
欢迎关注公众号【三戒纪元】