[英]Optimize integer and floating point multiplication
我正在尝试优化以下操作,其中我有大量无符号短输入,需要按某个因子按比例缩小。 有没有办法优化它不使用浮点运算
unsigned short val = 65523U;
val = val * 0.943;
注意
我将在DSP上进行上述操作,其中浮点运算成本很高
最简单的方法是使用可以保存结果的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 );
您可以乘以整数近似值0.943 * 2 ^ 16,然后除以2 ^ 16,编译器应将其转换为右移。 假设16位短路和至少32位整数:
val = ((unsigned)val * 61800) / 65536;
根据您的具体要求,您可以通过四舍五入到最接近的整数来获得更准确的结果:
val = ((unsigned)val * 61800 + 32768) / 65536;
任何其他两种力量都可以。 在64位平台上,您可以使用2 ^ 48来获得更高的精度。
多重/分裂的事情是好的。 但更好的是你可以避免分歧。
unisisned 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,使其恢复到正确的比例。
你可以乘以943
然后除以1000
。 你会保存一个浮点除法(但你要做乘法+一个欧几里德除法)。
unsigned short val = 65523U;
val = (val*943UL)/1000;
我得到: 61788
只要var*943
在unsigned long
容量内( unsigned long long
可用于进一步扩展限制),它就可以工作(即使在int
为16位宽的系统上)。
你可以乘以943
然后除以1000
。 你会保存一个浮点除法(但你要做乘法+一个欧几里德除法)。
unsigned short val = 65523U;
val = (val*943UL)/1000;
我得到: 61788
只要var*943
在unsigned long
容量内( unsigned long long
可用于进一步扩展限制),它就可以工作(即使在int
为16位宽的系统上)。
编辑:你甚至可以避免除法计算比率乘以2的幂,我选择了16:
所以.943*(1<<16)
是61800.448
你可以做一次乘法和一次换班操作(非常快)。 此时使用unsigned long long
更好,因为中间结果会变得非常大:
val = (val*61800UL)>>16;
得到大致相同的结果: 61787
。 使用61801
,你得到61788
使用32位int
或更高的平台,使用
int val = 65523U;
val = val * 943 / 1000;
很难被击败。 通过更改系数将截断转换为德语舍入。 如果你的系统有16位int
那么你可以使用long
(注意乘以943并除以1000将在long
算术中发生)但是解决方案需要分析。
先划分1000
会导致截断问题; 需要更大的类型来容纳更大的值。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.