C 在int溢出附近进行计算的智能方法

C 在int溢出附近进行计算的智能方法,c,integer-overflow,C,Integer Overflow,有什么聪明的方法可以解决这个问题吗 uint32_t a = 16637510; uint32_t b = 45627362; uint32_t c = 0; c = a * 100000 / b //overflows c = (a * 100/b)*1000 //gives 36000 我需要得到c=36463或更好的结果36464。需要快速、非浮动操作。CPU为stm32f4 更新: 接受的答案是将100000转换为100000ULL(64位),但正如@PeterJ所建议的(并删除了他的

有什么聪明的方法可以解决这个问题吗

uint32_t a = 16637510;
uint32_t b = 45627362;
uint32_t c = 0;
c = a * 100000 / b //overflows
c = (a * 100/b)*1000 //gives 36000
我需要得到c=36463或更好的结果36464。需要快速、非浮动操作。CPU为stm32f4

更新:

接受的答案是将100000转换为100000ULL(64位),但正如@PeterJ所建议的(并删除了他的答案),使用stm32f4 FPU比64除法运算更快

Timer t;
int i;
t.start();
for(i = 1; i <= 100000; ++i) c = a * 100000ULL / b;
t.stop();
printf("64\ttakes %f seconds, du is %d\n", t.read(), c);
t.reset();
t.start();
for(i = 1; i <= 100000; ++i) c = (uint32_t)((float)a * 100000.0f / (float)b);
t.stop();
printf("float\ttakes %f seconds, du is %d\n", t.read(), c);
t.reset();
定时器t;
int i;
t、 start();
对于(i=1;i这个怎么样

c = a * 100000ULL / b; // gives 36463

请参阅gcc为此操作生成的程序集以及溢出的原始
c=a*100000/b
。请注意,当64位数学代价高昂时,使用
\uuuuuabi\uldivmod
代替
\uuabi\uuidiv
时,有时仅32位的近似解可能会快得多。具体取决于过程ssor/编译器

让我们看看仅使用32位数学就可以完成什么


b==100000==0x186A0
让我们假设它是固定的——一个17位的数字

a==16637510==0x00FDDE46
,但OP说它在+/-1000范围内。因此它是一个24位的数字。
b
是一个26位的数字。有了这些限制,最后的商将始终在36464附近(一个16位的数字)

我们可以缩放乘积操作数
a,b
,以使用
a
的16位左右有效位和
b
的16位左右最高有效位,而不会丢失太多有效位。然后我们就有了一个16位*16位乘积,它不会溢出32位数学

我们可以利用
b
只有12个有效位的优势,让代码使用产品中24位
a
中最多20(32-12)个最高有效位

中间乘积是41位的,所以我们需要将乘法的规模缩小至少9位

#define SCALE_A 4
#define SCALE_M 5
// Insure SCALE_A + SCALE_M >= 9 to avoid overflow
// Perhaps other scales like SCALE_A 8, SCALE_M 1 will be faster.

uint32_t scale(uint32_t a, uint32_t b) {
  uint32_t product = (a >> SCALE_A)*(100000 >> SCALE_M);
  uint32_t c = product/(b >> (SCALE_A + SCALE_M));
  return c;
}

如果这对OP来说更快/更好?可能只是另一种考虑的方法。我将留给用户在线分析它的性能。

原始代码A是从输入捕获提姆获取的,所以它应该保持32。我会做一些速度测试来比较64位分区与你的浮点版本无关的烦恼。你不喜欢它-我删除它:存在近似的32位仅数学解。
a、b的范围是多少?可容忍的错误是什么(+/-1?)溢出有多常见?它们是一个例外还是每个数据集都会发生?@chux a,b将保持在+/-1000范围内错误(+/-1)是可以的。@PeterJ不要当孩子!只要像我一样证明你是对的。使用
(uint16_t)(a>>8)*(100000>>1)
可能允许使用16*16到32位的乘法作为发出的代码。