32 位整数缩放,无溢出

32bit integer scaling with no overrun

本文关键字:溢出 缩放 整数      更新时间:2023-10-16

嗨,我有以下值:

uint32_t value = 1230000;
uint32_t max_value = 1234567;

现在,我想执行缩放:

uint32_t scaled = value * 1000000 / max_value;

问题是如果我使用 32 位整数,它会溢出这么大的数字。 另一方面,我不能使用64位。知道如何正确实现提到的缩放吗?

编辑

值得一提的是,我正在研究STM32 - 一个32位微控制器,执行64位乘法和除法非常昂贵。因此,我想避免它们。

您可以通过将比例视为以 1000 为基数的数字来做到这一点:value = value_high*1000 + value_low 。分别计算 value_highvalue_low 对缩放的贡献,将中间值存储为分数:scaled = scaled_int + scaled_remainder/max_value

uint32_t value_low = value%1000;
uint32_t value_high = value/1000;
uint32_t scaled_int, scaled_remainder;
uint32_t low = value_low * 1000000;
scaled_int = low / max_value;
scaled_remainder = low % max_value;
scaled_int += value_high * 810;  // pre-calculated 1000*1000000 / max_value
scaled_remainder += value_high * 730;  // pre-calculated 1000*1000000 % max_value
scaled_int += scaled_remainder / max_value;
scaled_remainder = scaled_remainder % max_value;

此外,您不必使用基数 1000,基数 1024 可能会更快一些。只要第 4 行和第 8 行没有溢出其最大值,它应该可以工作。

你可以像下面这样将scaled声明为unsigned long long类型,然后相应地进行显式类型转换。

unsigned long long scaled = (unsigned long long)value * 1000000 / max_value; /* make either operand of unsigned long long type explicitly */
printf("%llun",scaled);

如果你的转换器可以支持64位,那么

uint32_t scaled =(uint32_t)((uint64_t)value * 1000000 / max_value);

如果支持浮动,则

uint32_t scaled =(uint32_t)(value *1.0 / max_value* 1000000);

会有一点精度损失。

最快的缩放方法是使用 2 个功率比例因子。

即使你有两个数字使用不同的比例因子缩放 - 算术是微不足道的。

例:

uint32_t scaledDiv(uint32_t a, uint32_t b, uint32_t *sclef)
{
    *sclef = __builtin_clz(a);
    a <<= *sclef;
    return a/b;
}

问题是形式上的:value/max_1 = x/1000000 .

如果不以封闭形式求解,则可以使用平分迭代计算最接近的x

 uint32_t step=max_value>>1;
 uint32_t v=0, x=0, step2=1000000/2 << 12;
 // step2 has been scaled to add extra precision
 while(step) {
    if (value > v+step) { v+=step; x+=step2; }
    step>>=1; step2>>=1;
 }
 x=(x+2048)>>12; // scale down and round

结果将在 32 次迭代中完成。