我在嵌入式系统中有一个算法需要用Q15定点算法计算sin(theta),sin(2 * theta),sin(3 * theta)等。 sin(theta)和cos(theta)是使用LUT /插值组合生成的,但是我使用Chebyshev方法计算高阶Sines,看起来像这样(伪代码):
sinN1 = Comes from Q15 LUT/interp algorithm
cosN1 = Comes from Q15 LUT/interp algorithm
sinN2 = (cosN1*sinN1)>>14;
sinN3 = (cosN1*sinN2)>>14 - sinN1;
sinN4 = (cosN1*sinN3)>>14 - sinN2;
....
问题在于,在某些条件下,此方法会产生可能溢出Q15变量的结果。例如,让我们考虑theta = 2.61697:
sinN1 (Q15) = int(2**15*sin(2.61697)) = 16413
cosN1 (Q15) = int(2**15*cos(2.61697)) = -28361
sinN2 = (-28361*16413)>>14 = -28412 # OK
sinN3 = (-28361*-28412)>>14 - 16413 = 32768 # OVERFLOW BY 1
..
我似乎从未超过一两个LSB。它似乎是复合量化的技艺。我使用的是ARM Cortex M4处理器,所以我可以用相对较少的指令添加饱和逻辑,但是我做了很多实时流式DSP,具有非常低的延迟要求所以我需要节省多少我可以使用CPU,所以我想知道是否有更优雅的方法来处理这个问题。
答案 0 :(得分:0)
我的数学家想建议保持对累积误差的估计,并以sigma-delta的方式对其进行校正,因为优雅,但我实用的程序员说这是非常不切实际的。
SSAT
指令会使您的结果饱和到您想要的任何位置,花费一个周期,并且应该可以通过任何非垃圾编译器上的__ssat
内在函数轻松获得。由于你不可避免地会使用任何非整数算术积累错误,我不确定除了进行计算和花费一个额外周期以确保它在范围内之外还有什么更好的。
我不能完全解决的问题是,如果你可以通过一些(内联)程序集来完全免费获得它,以获得内部不会暴露的ssat
中的可选移位,在乘法/ FP校正步骤是最大误差源的基础上;像(伪装配):
mul tmp, cosN1, sinN2
ssat tmp, #16, tmp, asr #14
sub sinN3, tmp, sinN1
如果你能保证永远不会得到像sinN3 = 0 - (-32768)之类的东西,那么你只能安全地逃脱。 QSUB16
很容易替换SUB
,但是对于符号位的上半部分,并且只要添加掩码或半字包装指令来纠正你已经失去了“免费”游戏。