在int溢出附近进行计算的聪明方法

时间:2017-07-06 17:42:50

标签: 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建议的那样(并删除了他的答案)使用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();
  

64需要0.086669秒,du是57333
  float需要0.017779秒,du是57333

2 个答案:

答案 0 :(得分:1)

这个怎么样?

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

有关gcc为此操作生成的程序集以及溢出的原始c = a * 100000 / b,请参阅https://godbolt.org/g/aemCyw。请注意,__aeabi_uldivmod代替__aeabi_uidiv

答案 1 :(得分:0)

当64位数学成本很高时,有时32位的近似解决方案可能会明显加快。取决于处理器/编译器。

让我们看看只使用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更快/更好?也许。只需另一种方法来考虑。我会留给用户在线进行性能分析。