Main Content

定点算术运算

定点算术

加减法

将两个定点数相加时,您可能需要一个进位位来正确表示结果。因此,将两个 B 位数(具有相同的定标)相加时,与使用两个操作数时相比,结果值有一个额外的位。

a = fi(0.234375,0,4,6);
c = a+a
c = 

    0.4688

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Unsigned
            WordLength: 5
        FractionLength: 6
a.bin
ans =

1111
c.bin
ans =

11110

如果对具有不同精度的两个数字执行加法或减法,首先需要对齐小数点才能执行运算。结果是:运算结果与操作数之间存在多于一位的差异。

a = fi(pi,1,16,13);
b = fi(0.1,1,12,14);
c = a + b
c = 

    3.2416

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 18
        FractionLength: 14

乘法

通常,全精度乘积需要的字长等于各操作数字长之和。在以下示例中,请注意,乘积 c 的字长等于 a 的字长加上 b 的字长。c 的小数长度也等于 a 的小数长度加上 b 的小数长度。

a = fi(pi,1,20), b = fi(exp(1),1,16)
a = 

    3.1416

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 20
        FractionLength: 17

b = 

    2.7183

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 16
        FractionLength: 13
c = a*b
c = 

    8.5397

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 36
        FractionLength: 30

与其他内置数据类型的数学运算

注意,在 C 语言中,整数数据类型和双精度数据类型之间的运算结果会提升为双精度类型。但是,在 MATLAB® 中,内置的整数数据类型和双精度数据类型之间的运算结果是整数。在这方面,fi 对象的行为与 MATLAB 中的内置整数数据类型相似。

fidouble 之间进行加法运算时,双精度会转换为与原 fi 输入具有相同数值类型的 fi。该运算的结果是 fi。当在 fidouble 之间进行乘法运算时,双精度会转换为 fi,其字长和符号性与原 fi 相同且具有最佳精度的小数长度。该运算的结果是 fi

a = fi(pi);
a = 

    3.1416

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 16
        FractionLength: 13
b = 0.5 * a
b = 

    1.5708

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 32
        FractionLength: 28

在内置整数数据类型 [u]int[8, 16, 32] 之一与 fi 之间进行算术运算时,保留整数的字长和符号性。该运算的结果是 fi

a = fi(pi);
b = int8(2) * a
b = 

    6.2832

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 24
        FractionLength: 13

fi 与逻辑数据类型之间进行算术运算时,逻辑值被视为值为 0 或 1 且字长为 1 的无符号 fi 对象。该运算的结果是 fi 对象。

a = fi(pi);
b = logical(1);
c = a*b
c = 

    3.1416

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 17
        FractionLength: 13

fimath 对象

fimath 属性定义对 fi 对象执行算术运算的规则,包括数学、舍入和溢出属性。fi 对象可以有局部 fimath 对象,它也可以使用默认 fimath 属性。您可以使用 setfimathfimath 对象附加到 fi 对象。您也可以在创建时在 fi 构造函数中指定 fimath 属性。当 fi 对象具有局部 fimath 而不是使用默认属性时,fi 对象的显示中将显示 fimath 属性。在此示例中,a 具有在构造函数中指定的 ProductMode 属性。

 a = fi(5,1,16,4,'ProductMode','KeepMSB')
a = 

     5

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 16
        FractionLength: 4

        RoundingMethod: Nearest
        OverflowAction: Saturate
           ProductMode: KeepMSB
     ProductWordLength: 32
               SumMode: FullPrecision
aProductMode 属性设置为 KeepMSB,而其余的 fimath 属性使用默认值。

注意

有关 fimath 对象及其属性和默认值的详细信息,请参阅 fimath Object Properties

位增长

下表显示 fi 对象 ABSumModeProductMode 属性使用默认 fimathFullPrecision 时的位增长。

 ABSum = A+BProd = A*B
格式fi(vA,s1,w1,f1)fi(vB,s2,w2,f2)
符号s1s2Ssum = (s1||s2)Sproduct = (s1||s2)
整数位I1 = w1-f1-s1I2= w2-f2-s2Isum = max(w1-f1, w2-f2) + 1 - SsumIproduct = (w1 + w2) - (f1 + f2)
小数位f1f2Fsum = max(f1, f2) Fproduct = f1 + f2
总位数w1w2Ssum + Isum + Fsumw1 + w2

此示例说明在 for 循环中发生的位增长。

T.acc = fi([],1,32,0);
T.x = fi([],1,16,0);

x = cast(1:3,'like',T.x);
acc = zeros(1,1,'like',T.acc);

for n = 1:length(x)
    acc = acc + x(n)
end
acc = 

     1
      s33,0

acc = 

     3
      s34,0

acc = 

     6
      s35,0
随着循环的每次迭代,acc 的字长也随之增加。这种增加会导致两个问题:一个是代码生成不允许在循环中更改数据类型。另一个是,如果循环足够长,则会在 MATLAB 中耗尽内存。请参阅控制位增长了解避免此问题的一些策略。

控制位增长

使用 fimath

通过指定 fi 对象的 fimath 属性,您可以控制在对对象执行运算时的位增长。

F = fimath('SumMode', 'SpecifyPrecision', 'SumWordLength', 8,...
 'SumFractionLength', 0);
a = fi(8,1,8,0, F);
b = fi(3, 1, 8, 0);
c = a+b
c = 

    11

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 8
        FractionLength: 0

        RoundingMethod: Nearest
        OverflowAction: Saturate
           ProductMode: FullPrecision
               SumMode: SpecifyPrecision
         SumWordLength: 8
     SumFractionLength: 0
         CastBeforeSum: true

fi 对象 a 具有局部 fimath 对象 FF 指定和的字长和小数长度。在默认 fimath 设置下,输出 c 通常字长为 9,小数长度为 0。但是,由于 a 具有局部 fimath 对象,因此生成的 fi 对象的字长为 8,小数长度为 0。

您还可以使用 fimath 属性来控制 for 循环中的位增长。

F = fimath('SumMode', 'SpecifyPrecision','SumWordLength',32,...
'SumFractionLength',0);
T.acc = fi([],1,32,0,F);
T.x = fi([],1,16,0);

x = cast(1:3,'like',T.x);
acc = zeros(1,1,'like',T.acc);

for n = 1:length(x)
    acc = acc + x(n)
end
acc = 

     1
      s32,0

acc = 

     3
      s32,0

acc = 

     6
      s32,0

T.acc 使用默认 fimath 属性时不同,acc 的位增长现在受到限制。因此,acc 的字长保持为 32。

下标赋值

控制位增长的另一种方法是使用下标赋值。a(I) = bb 的值赋给由下标向量 I 指定的 a 的元素,同时保留 anumerictype

T.acc = fi([],1,32,0);
T.x = fi([],1,16,0);

x = cast(1:3,'like',T.x);
acc = zeros(1,1,'like',T.acc);

% Assign in to acc without changing its type
for n = 1:length(x)
    acc(:) = acc + x(n)
end

acc (:) = acc + x(n) 指示下标向量 (:) 处的值发生更改。但是,输出 accnumerictype 保持不变。由于 acc 是标量,因此如果您使用 (1) 作为下标向量,则也会收到相同的输出。

  for n = 1:numel(x)
    acc(1) = acc + x(n);
  end
acc = 

     1
      s32,0

acc = 

     3
      s32,0

acc = 

     6
      s32,0

accnumerictypefor 循环的每次迭代中保持不变。

下标赋值还可以帮助您控制函数中的位增长。在函数 cumulative_sum 中,ynumerictype 不会更改,但由 n 指定的元素中的值会更改。

function y = cumulative_sum(x)
% CUMULATIVE_SUM Cumulative sum of elements 
% of a vector.
%
%   For vectors, Y = cumulative_sum(X) is a 
%   vector containing the cumulative sum of 
%   the elements of X.  The type of Y is the type of X.
    y = zeros(size(x),'like',x);
    y(1) = x(1);
    for n = 2:length(x)
        y(n) = y(n-1) + x(n);
    end
end
y = cumulative_sum(fi([1:10],1,8,0))
y = 

     1     3     6    10    15    21    28    36    45    55

          DataTypeMode: Fixed-point: binary point scaling
            Signedness: Signed
            WordLength: 8
        FractionLength: 0

注意

有关下标赋值的详细信息,请参阅 subsasgn 函数。

accumposaccumneg

控制位增长的另一种方法是使用 accumposaccumneg 函数来执行加法和减法运算。与使用下标赋值类似,accumposaccumneg 保留其输入 fi 对象之一的数据类型,同时允许您指定舍入方法和输入值中的溢出操作。

有关如何实现 accumposaccumneg 的详细信息,请参阅Avoid Multiword Operations in Generated Code

溢出和舍入

执行定点算术运算时,需要考虑溢出的可能性和后果。fimath 对象指定执行算术运算时使用的溢出和舍入模式。

溢出

当运算结果超过最大或最小可表示值时,可能会发生溢出。fimath 对象具有 OverflowAction 属性,它提供两种处理溢出的方法:饱和与绕回。如果将 OverflowAction 设置为 saturate,则溢出会通过饱和方式限制为该范围内的最大值或最小值。如果将 OverflowAction 设置为 wrap,则任何溢出都将绕回,对于无符号值,会采用模运算绕回,对于有符号值,则采用 2 的补码绕回。

有关如何检测溢出的详细信息,请参阅Underflow and Overflow Logging Using fipref

舍入

选择舍入方法时需要考虑几个因素,包括成本、偏置以及是否存在溢出的可能性。Fixed-Point Designer™ 软件提供了几个不同舍入函数来满足您的设计要求。

舍入方法 描述成本偏置是否可能溢出
ceil 朝正无穷方向舍入到最邻近的可表示数字。大的正向偏置
convergent舍入到最邻近的可表示数字。在舍入机会均等的情况下,convergent 舍入到最邻近的偶数。这种方法是由工具箱提供的最小偏置舍入方法。无偏置
floor朝负无穷方向舍入到最邻近的可表示数字,相当于 2 的补码截断。大的负向偏置
nearest舍入到最邻近的可表示数字。在舍入机会均等的情况下,nearest 朝正无穷方向舍入到最邻近的可表示数字。此舍入方法是 fi 对象创建和 fi 算术的默认值。中等小的正向偏置
round

舍入到最邻近的可表示数字。在舍入机会均等的情况下,round 方法进行如下舍入:

  • 将正数朝正无穷方向舍入到最邻近的可表示数字。

  • 将负数朝负无穷方向舍入到最邻近的可表示数字。

  • 对于具有负值的样本,为小的负偏置

  • 对于具有均匀分布的正值和负值的样本,无偏置

  • 对于具有正值的样本,为小的正向偏置

fix舍入到零方向上最邻近的可表示数字。
  • 对于具有负值的样本,为大的正向偏置

  • 对于具有均匀分布的正值和负值的样本,无偏置

  • 对于具有正值的样本,为大的负向偏置