为什么int * float比int / int更快?

3

我阅读了一些关于在小型Arduino微控制器上使用浮点数运算不好的文章。因此,在尝试减少浮点数使用量时,我发现了一件奇怪的事情。

// Baseline
float brightness = 0.05;
int result = someInt * brightness;

// Takes about twice as long
int brightness = 20;
int result = someInt / brightness;

两者的目标都是将整数降低到原始值的二十分之一,但在进行数学优化时,我不确定为什么浮点数速度更快。


你是怎么测量它的? - R.. GitHub STOP HELPING ICE
2
整数除法可能会非常慢。 - M.M
虽然这不是一个全面的答案(因此我发布为评论),但以下链接看起来可能会有用:http://forum.arduino.cc/index.php?topic=92684.0 基本上,整数除法(int)比整数乘法慢50倍以上 - 这是由于除法没有硬件指令。 (该评论声称存在mul指令,尽管我没有费心阅读328p的数据表,也不知道或评论此事)有很多时间可以执行浮点乘法的软件实现。 - enhzflep
这是给你的:http://ridiculousfish.com/blog/posts/labor-of-division-episode-iii.html - user1196549
4个回答

3
您可以使用乘法和位移运算代替除法。
int brightness = 20;
int multer = 256/20;
int result = (someInt * multer) >> 8;

使用更多的“multer”位数,可以获得更精确的结果。

那么,总的来说,我可以通过将0-1映射到0-0xFF并执行(int8val * int8fract) >> 8来获得快速整数除法。是这样吗?如果两者都是16位呢?(int16val * int16fract) >> 16也可以工作吗? - Alex Wayne
您可以映射到任何精度,不仅限于0-0xFF,而是对于int32和uint8源/结果为0-0xFFFFFF。(int16val * int16fract)>> 16 - 是的,它也可以工作,但请注意,如果您使用有符号整数,则应该使用1位少用于符号。 - minorlogic
虽然@EkriirkE提供了更好的答案来回答实际问题,但这是一个非常有用的提示。非常感谢! - Alex Wayne

3
原生AVR (Arduino)没有乘法或除法函数,更别提原生浮点数处理了。当你在进行乘法运算时,本质上只是一堆加法。使用减法计算除法更为困难(不能走得太远),还要找到余数 - 如果是浮点数,则从余数中计算分数,这本身就需要大量的加/乘和减法。
浮点数也很慢/不好,因为C库必须仅使用整数进行内部计算,即使涉及小数位的处理。你会注意到,任何使用浮点数的东西都会极大地增加程序大小(添加浮点数库)。你应该发现,浮点数除法甚至比整数除法更慢。
不知怎么的,使用带有所有分数开销的浮点库进行乘法运算,效率更高。

如果 int 除法很慢,而使用 float 进行任何操作也很慢,那么是否有一种标准且被接受的超级快速整数乘以有理数的方法? - Alex Wayne
@AlexWayne 可能有点晚了,但我会建议找一个使用二的幂次作为分母的有理数。然后您可以使用位移来除以这个数,只需要整数乘法,应该更快。 - penguin359

1
你正在比较乘法和除法。这就像比较苹果和梨。
要除以20,你可以使用整数乘以floor(2^k/5)ceiling(2^k/5),然后右移k+2位。选择不会导致溢出的最大k

0

你可以使用位移运算来进行除法。参考https://dev59.com/6m035IYBdhLWcg3wNdLg#19076173,其中包含了一个除以10的函数:

unsigned divu10(unsigned n) {
    unsigned q, r;
    q = (n >> 1) + (n >> 2);
    q = q + (q >> 4);
    q = q + (q >> 8);
    q = q + (q >> 16);
    q = q >> 3;
    r = n - (((q << 2) + q) << 1);
    return q + (r > 9);
}

所以这应该被20除:

unsigned divu20(unsigned n) {
    return divu10(n)>>1;
}

位移操作应该比乘法或除法指令更快 - 特别是如果(正如EkriirkE已经说过的那样)“本地AVR(arduino)没有乘法或除法函数”


1
您还可以使用乘法来除以任何整数常量,实际上在大多数情况下,gcc会为您执行此转换... - R.. GitHub STOP HELPING ICE
我通常将常量写成数学形式,因为预编译器会识别它并将结果编译为常量,例如 #define x ((100+33)*0.22) 如果这些数字有意义,我想微调而不重新计算。 - EkriirkE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接