定点数的乘法及格式转换

定点数的乘法及格式转换

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$位被解除

注意:这个操作可能发生溢出,这时,通常会执行饱和运算

  1. $ S_{{n_1}·{m_1}} 是正数,将转换结果设置为 是正数,将转换结果设置为 是正数,将转换结果设置为 S_{{n_2}·{m_2}} $可以表示的最大正数;
  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 m2m1个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 m1m2个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)

欢迎关注公众号【三戒纪元】

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值