我正在尝试优化以下操作,其中我有大量无符号短输入,需要按某个因子按比例缩小。有没有办法优化它以不使用浮点运算
unsigned short val = 65523U;
val = val * 0.943;
注意
我将在浮点运算成本高昂的DSP上运行以上
答案 0 :(得分:8)
最简单的方法是使用可以保存结果的32位类型:
uint16_t val = 65523U;
val = (uint_fast32_t)val * 943 / 1000;
或者如果你想要更多类型的正确性和可移植性,同时允许编译器为任务使用最好的整数类型:
#include <stdint.h>
uint_fast16_t val = UINT16_C(65523);
val = (uint_fast16_t) ( (uint_fast32_t)val * (uint_fast32_t)943 / (uint_fast32_t)1000 );
答案 1 :(得分:8)
您可以乘以整数近似值0.943 * 2 ^ 16,然后除以2 ^ 16,您的编译器应将其转换为右移。假设16位短路和至少32位整数:
val = ((unsigned)val * 61800) / 65536;
根据您的具体要求,您可以通过四舍五入到最接近的整数来获得更准确的结果:
val = ((unsigned)val * 61800 + 32768) / 65536;
任何其他两种力量都可行。在64位平台上,您可以使用2 ^ 48来获得更高的精度。
答案 2 :(得分:2)
多重/分裂的事情是好的。但更好的是你可以避免分歧。
unisgned short的范围为0 ... 65535.
CPU中的所有数学计算都在内部处理为32位数。但是在计算之后它们会被抛回到16位。如果您将短数乘以大数,则需要避免这种情况。输出将很短,导致它截断该值。所以我把演员阵容展示出去了,并确保编译器没有额外的类型转换。
unsigned short val = 65523U;
const unsigned int mult = 65536 * 0.943; // expressed as a fraction of 2^16
unsigned short output = (unsigned short)(((unsigned int)val * mult) >> 16));
所以这会将值转换为32位无符号整数(以保证对类型的控制),根据原始分数将其乘以最多2 ^ 16,然后将其右移16以将其重新置于正确的位置规模。
答案 3 :(得分:1)
您可以乘以943
然后除以1000
。你保存一个浮点除法(但是你要做乘法+一个欧几里德除法)。
unsigned short val = 65523U;
val = (val*943UL)/1000;
我得到:61788
int
在var*943
容量范围内(unsigned long
可用于扩展限制,就可以工作(即使在unsigned long long
为16位宽的系统上)更进一步)。
您可以乘以943
然后除以1000
。你保存一个浮点除法(但是你要做乘法+一个欧几里德除法)。
unsigned short val = 65523U;
val = (val*943UL)/1000;
我得到:61788
int
在var*943
容量范围内(unsigned long
可用于扩展限制,就可以工作(即使在unsigned long long
为16位宽的系统上)更进一步)。
编辑:你甚至可以避免除法计算比率乘以2的幂,我选择了16:
所以.943*(1<<16)
61800.448
你可以做一次乘法和一次换班操作(非常快)。此时使用unsigned long long
会更好,因为中间结果会变得非常大:
val = (val*61800UL)>>16;
获得大致相同的结果:61787
。使用61801
即可获得61788
答案 4 :(得分:1)
使用32位int
或更高版本的平台,使用
int val = 65523U;
val = val * 943 / 1000;
很难被击败。通过更改系数将截断转换为德语舍入。如果您的系统有一个16位int
,那么您可以使用long
(请注意,乘以943并除以1000将在long
算术中进行),但解决方案需要分析
首先除以1000
会导致截断问题;需要更大的类型来容纳更大的值。