定点数的乘法及格式转换

定点数的乘法及格式转换

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)

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

### 定点数乘法在FPGA上的实现 #### 小数定点化的背景 在FPGA中,处理小数通常采用定点数的方式,这是因为直接使用浮点数会显著增加硬件资源的消耗。定点数通过固定小数点的位置来简化运算过程[^1]。 #### 定点数乘法的基本原理 对于定点数乘法操作,其核心思想在于将数值转换为二进制形式并忽略小数点的存在进行计算。完成基本的整数乘法之后,再根据输入数据中小数点的位置重新定位结果中的小数点位置[^2]。 #### FPGA上定点数乘法的具体实现方法 以下是几种常见的实现方式及其优缺点分析: ##### 方法一:直接乘法 这是最直观的方法,即将两个定点数按照普通的二进制乘法规则执行运算。此方法的优点是易于理解和编程实现,但它的缺点也很明显——较高的资源消耗和较差的时序性能。例如,在某些情况下,这种方法可能需要多达252个LUT(查找表)资源[^3]。 ```verilog module direct_multiply ( input [9:0] a, b, output reg [19:0] result ); always @(*) begin result = a * b; end endmodule ``` ##### 方法二:移位替代乘法 利用幂次关系特性,可以通过简单的移位操作代替部分复杂的乘法运算。比如要实现`a × 8`这样的操作可以直接左移三位即可得到相同的效果。这种方式能够有效减少所需的组合逻辑资源量至约217 LUTs,并改善时序延迟到-0.630 ns级别。 ##### 方法三:位拼接与加法结合 当遇到较为特殊的系数时(如15),可以将其分解成多个较小单位之和的形式(`8+4+2+1`)并通过多次累加达到最终目标值的目的。虽然理论上该策略同样具备较低的硬件开销优势,但在实际应用过程中需注意过多连续叠加可能会引发新的问题—过长的数据路径可能导致无法满足严格的时钟周期需求。 #### 正负数之间的乘法处理 针对涉及正负号判定场景下的定点数相乘情形,还需要额外考虑以下几个方面: 1. **预处理阶段**:任何参与运算前都应先把所有负值转为其绝对值对应的补码表达形式; 2. **主体流程遵循无符号整型规则开展具体算术活动**; 3. **后置调整环节**:依据原始两因子各自的极性状况决定产物总标志状态,并据此补充必要的扩展填充动作以确保输出宽度一致性和准确性[^4]。 --- ### 示例代码展示 下面给出一段综合运用上述技术要点编写而成的实际Verilog HDL源程序片段供参考学习: ```verilog // Example of signed fixed-point multiplication using shift and add approach on FPGA. module fixed_point_multiplier( input wire signed [9:0] multiplicand, multiplier, output reg signed [19:0] product ); reg signed [9:0] abs_multiplicand, abs_multiplier; initial begin if(multiplicand[9]) abs_multiplicand = ~multiplicand + 1; // Convert to positive by taking two's complement else abs_multiplicand = multiplicand; if(multiplier[9]) abs_multiplier = ~multiplier + 1; else abs_multiplier = multiplier; end always @(abs_multiplicand or abs_multiplier) begin case(abs_multiplier) 10'b0000_0000_00 : product = {1'd0,{abs_multiplicand}} << 0 ; 10'b0000_0000_01 : product = {1'd0,{abs_multiplicand}} << 1 ; default : product = 'bx ; // Undefined behavior for other cases endcase // Determine final sign based XOR operation between original inputs' signs bits if((multiplicand[9]^multiplier[9])) product = -(product); // Apply negative sign when necessary end endmodule ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值