高效的程序,可在嵌入式上下文中缩放值

问题描述

我想在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时不会。