定点算术运算
定点算术
加减法
将两个定点数相加时,您可能需要一个进位位来正确表示结果。因此,将两个 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: 6a.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: 13c = a*b
c =
8.5397
DataTypeMode: Fixed-point: binary point scaling
Signedness: Signed
WordLength: 36
FractionLength: 30与其他内置数据类型的数学运算
注意,在 C 语言中,整数数据类型和双精度数据类型之间的运算结果会提升为双精度类型。但是,在 MATLAB® 中,内置的整数数据类型和双精度数据类型之间的运算结果是整数。在这方面,fi 对象的行为与 MATLAB 中的内置整数数据类型相似。
在 fi 和 double 之间进行加法运算时,双精度会转换为与原 fi 输入具有相同 numerictype 的 fi。该运算的结果是 fi。当在 fi 和 double 之间进行乘法运算时,双精度会转换为 fi,其字长和符号性与原 fi 相同且具有最佳精度的小数长度。该运算的结果是 fi。
a = fi(pi);
a =
3.1416
DataTypeMode: Fixed-point: binary point scaling
Signedness: Signed
WordLength: 16
FractionLength: 13b = 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: 13fimath 对象
fimath 属性定义对 fi 对象执行算术运算的规则,包括数学、舍入和溢出属性。fi 对象可以有局部 fimath 对象,它也可以使用默认 fimath 属性。您可以使用 setfimath 将 fimath 对象附加到 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: FullPrecisiona 的 ProductMode 属性设置为 KeepMSB,而其余的 fimath 属性使用默认值。注意
有关 fimath 对象及其属性和默认值的详细信息,请参阅 fimath Object Properties。
位增长
下表显示 fi 对象 A 和 B 的 SumMode 和 ProductMode 属性使用默认 fimath 值 FullPrecision 时的位增长。
| A | B | Sum = A+B | Prod = A*B | |
|---|---|---|---|---|
| 格式 | fi(vA,s1,w1,f1) | fi(vB,s2,w2,f2) | — | — |
| 符号 | s1 | s2 | Ssum = (s1||s2) | Sproduct = (s1||s2) |
| 整数位 | I1 = w1-f1-s1 | I2= w2-f2-s2 | Isum = max(w1-f1, w2-f2) + 1 - Ssum | Iproduct = (w1 + w2) - (f1 + f2) |
| 小数位 | f1 | f2 | Fsum = max(f1, f2) | Fproduct = f1 + f2 |
| 总位数 | w1 | w2 | Ssum + Isum + Fsum | w1 + 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,0acc 的字长也随之增加。这种增加会导致两个问题:一个是代码生成不允许在循环中更改数据类型。另一个是,如果循环足够长,则会在 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: truefi 对象 a 具有局部 fimath 对象 F。F 指定和的字长和小数长度。在默认 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) = b 将 b 的值赋给由下标向量 I 指定的 a 的元素,同时保留 a 的 numerictype。
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) 指示下标向量 (:) 处的值发生更改。但是,输出 acc 的 numerictype 保持不变。由于 acc 是标量,因此如果您使用 (1) 作为下标向量,则也会收到相同的输出。
for n = 1:numel(x) acc(1) = acc + x(n); end
acc =
1
s32,0
acc =
3
s32,0
acc =
6
s32,0acc 的 numerictype 在 for 循环的每次迭代中保持不变。
下标赋值还可以帮助您控制函数中的位增长。在函数 cumulative_sum 中,y 的 numerictype 不会更改,但由 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 函数。
accumpos 和 accumneg
控制位增长的另一种方法是使用 accumpos 和 accumneg 函数来执行加法和减法运算。与使用下标赋值类似,accumpos 和 accumneg 保留其输入 fi 对象之一的数据类型,同时允许您指定舍入方法和输入值中的溢出操作。
有关如何实现 accumpos 和 accumneg 的详细信息,请参阅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 | 舍入到最邻近的可表示数字。在舍入机会均等的情况下,
| 高 |
| 是 |
fix | 舍入到零方向上最邻近的可表示数字。 | 低 |
| 否 |