[英]32bit integer scaling with no overrun
Hi I have a following values: 嗨,我有以下值:
uint32_t value = 1230000;
uint32_t max_value = 1234567;
Now, I would like to perform scaling: 现在,我想执行缩放:
uint32_t scaled = value * 1000000 / max_value;
The problem is if I use 32bit integer it will overrun for such big numbers. 问题是,如果我使用32位整数,那么对于这么大的数字它将溢出。 On the other hand I cannot use 64bit.
另一方面,我不能使用64位。 Any idea how to properly implement mentioned scaling?
任何想法如何正确实现上述缩放?
EDIT 编辑
Just to mention, I am working on STM32 - a 32-bit micro-controller, where performing 64-bit multiplication and division is extremely costly. 值得一提的是,我正在研究STM32(32位微控制器),在其中执行64位乘法和除法非常昂贵。 Therefore I would like to avoid them.
因此,我想避免它们。
You can do it by seeing scale as a base-1000 number: value = value_high*1000 + value_low
. 您可以通过将scale作为一个以1000为底的数字来实现:
value = value_high*1000 + value_low
。 Calculate value_high
's and value_low
's contribution to scaled separately, storing the intermediate values as a fraction: scaled = scaled_int + scaled_remainder/max_value
分别计算
value_high
和value_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;
Also, you don't have to use base 1000, base 1024 might be a bit faster. 此外,您不必使用以1000为基数,以1024为基数可能会更快一些。 As long as the 4th and 8th line don't overflow at their maximum value, it should work.
只要第4行和第8行不溢出其最大值,它就应该起作用。
you can declare scaled
as unsigned long long
type like below & then do explicit typecast accordingly. 您可以将
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("%llu\n",scaled);
if your translater can support 64bit,then 如果您的翻译器可以支持64位,则
uint32_t scaled =(uint32_t)((uint64_t)value * 1000000 / max_value);
if support float,then 如果支持浮动,则
uint32_t scaled =(uint32_t)(value *1.0 / max_value* 1000000);
There will be a little loss of precision. 会有一点精度损失。
The fastest way of scaling is using the 2 power scale factors. 最快的缩放方法是使用2个功率缩放因子。
Even you have two numbers scaled using different scale factors - the arithmetic is trivial. 即使您有两个使用不同比例因子缩放的数字-该算法也是微不足道的。
example: 例:
uint32_t scaledDiv(uint32_t a, uint32_t b, uint32_t *sclef)
{
*sclef = __builtin_clz(a);
a <<= *sclef;
return a/b;
}
The problem is of form: value/max_1 = x/1000000
. 问题的形式为:
value/max_1 = x/1000000
。
Without solving this in closed form, the closest x
can be calculated iteratively using bisection. 无需以封闭形式求解此问题,就可以使用二等分迭代地计算最接近的
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
The result will finish in 32 iterations. 结果将在32次迭代中完成。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.