问题描述
我想在0到32767之间缩放输入值。就上下文而言,该程序在ARM M0上运行,我希望避免任何浮点运算。我的输入类型是u32,输出类型是u16。到目前为止,我已经做到了,在做大乘法时,我明确检查了溢出。如果没有,我只是简单地将数字相乘。这种检查溢出的方法是否是实现所需比例缩放的有效方法?
uint16_t scaling(uint32_t in,uint32_t base)
{
uint32_t new_base = INT16_MAX; // 32767
if (in >= base) {
return (uint16_t)new_base;
}
if (in != ((in * new_base) / new_base) ) {
// overflow,increase bit width!
uint64_t tmp = in * (uint64_t)new_base;
tmp /= base;
return (uint16_t)(tmp);
}
// simply multiply
uint32_t tmp = (in * new_base) / base;
if (tmp > new_base) {
tmp = new_base; // clamp
}
return (uint16_t)tmp;
}
例如,对于样本“大”输入(in = 200000,base = 860000),预期输出为7620(此程序确实会返回)
解决方法
OP代码的以下变体会预先计算in * new_base
溢出到64位的阈值,并保存最终的检查if (tmp > new_base)
,该检查一次in < base
是多余的。
uint16_t scaling(uint32_t in,uint32_t base)
{
static const uint32_t new_base = INT16_MAX; // 2^15 - 1 = 32767
static const uint32_t in32 = (uint32_t)(((uint64_t)UINT32_MAX + 1) / INT16_MAX); // 2^32 / (2^15 - 1) = 131076
if (in >= base) {
return (uint16_t)new_base;
}
if(in < in32) {
return (uint16_t)((in * new_base) / base);
}
return (uint16_t)(((uint64_t)in * new_base) / base);
}
作为旁注,当in * new_base
被声明为{{1时,至少one compiler(使用默认优化运行)将用便宜的(in << 15) - in
替换乘法new_base = INT16_MAX
}},但仅声明为const uint32_t
时不会。