在C+;中使用%运算符和/运算符的替代方法+; P>在嵌入式C++中,模运算符“%”和除运算符“/”是非常低效的。

在C+;中使用%运算符和/运算符的替代方法+; P>在嵌入式C++中,模运算符“%”和除运算符“/”是非常低效的。,c++,modulo,processing-efficiency,C++,Modulo,Processing Efficiency,我如何实现以下表达式: a = b % c; 我理解这可以通过以下逻辑实现: a = b - c; while (a >= c) { a = a - c; } 但我的问题是,与%运算符相比,这段涉及while循环的代码是否足够有效 谢谢, Kirti没有什么比%操作符更高效的了。如果有更好的方法,那么任何合理的编译器都会自动转换它。当你被告知%和/效率低下时,那只是因为这些操作都很困难——如果你需要执行模运算,那么就这样做 在某些特殊情况下,可能有更好的方法——例如,mod a二次

我如何实现以下表达式:

a = b % c;
我理解这可以通过以下逻辑实现:

a = b - c;
while (a >= c) {
  a = a - c;
}

但我的问题是,与%运算符相比,这段涉及while循环的代码是否足够有效

谢谢,
Kirti

没有什么比
%
操作符更高效的了。如果有更好的方法,那么任何合理的编译器都会自动转换它。当你被告知
%
/
效率低下时,那只是因为这些操作都很困难——如果你需要执行模运算,那么就这样做


在某些特殊情况下,可能有更好的方法——例如,mod a二次幂可以写成二进制或二进制——但这些方法可能是由编译器优化的。

除法和模确实是代价高昂的硬件操作,无论您做什么(这与硬件体系结构的关系比与语言或编译器的关系更大),也许比加法慢十倍

然而,在当前的笔记本电脑或服务器上,以及在高端微控制器上,未命中通常比分区慢得多

当除数为常数时,GCC编译器通常能够对其进行优化

您的朴素循环通常比使用硬件分割指令(或库例程执行此操作,如果不是由硬件提供的话)慢得多。我相信你在避免分割&用你的循环替换它是错误的


你可以调整你的算法-例如,通过使用2的力量-但我不建议使用你的代码。请记住,过早优化是有害的因此,请首先尝试使您的程序正确,然后对其进行分析以找出故障点。

这段代码几乎肯定会比处理器/编译器决定执行除法/mod的速度慢。一般来说,对于基本的算术运算符来说,很难找到快捷方式,因为mcu/cpu设计者和编译器程序员非常擅长对几乎所有应用程序的快捷方式进行优化

嵌入式设备中的一个常见快捷方式(每个周期/字节都会产生差异)是将所有内容都保留在base-2中,以使用位移位运算符执行乘法和除法,并使用位and(&)执行模运算

示例:

unsigned int x = 100;
unsigned int y1 = x << 4;   // same as x * 2^4 = x*16
unsigned int y2 = x >> 6;   // same as x / 2^6 = x/64
unsigned int y3 = x & 0x07; // same as x % 8
无符号整数x=100;
无符号整数y1=x>6;//与x/2^6=x/64相同
无符号整数y3=x&0x07;//与x%8相同

如果除数在编译时已知,则可以通过一些移位、加法和其他快速运算将运算转换为倒数乘法。这在任何现代处理器上都会更快,即使它在硬件上实现了分区。嵌入式目标通常具有高度优化的除法/模运算例程,因为这些操作是标准要求的。

如果其他目标的幂为2或多加移位组合,则可通过移位实现常数除法


http://masm32.com/board/index.php?topic=9937.0包含x86汇编版本以及从第一篇文章下载的C源代码。为您生成此代码的

如果您仔细分析了代码,发现模运算符是内部循环的主要成本,那么有一种优化可能会有所帮助。您可能已经熟悉了使用算术左移位(对于32位值)确定整数符号的技巧:

这会将符号位扩展到整个字,因此负值产生-1,正值产生0。然后设置位0,使正值产生1

如果我们只增加一个小于模的量,那么同样的技巧可以用来包装结果:

val += inc;
val -= modulo & ( static_cast< int32_t >( ( ( modulo - 1 ) - val ) ) >> 31 );
val+=inc;
val-=模和(静态转换((模-1)-val))>>31);
或者,如果递减的值小于模,则相关代码为:

int32_t signedVal = static_cast< int32_t >( val - dec );
val = signedVal + ( modulo & ( signedVal >> 31 ) );
int32\u t signedVal=static\u cast(val-dec);
val=signedVal+(模和(signedVal>>31));
我添加了static_cast操作符,因为我要传入uint32_t,但您可能觉得它们不是必需的


与简单的%运算符相比,这有帮助吗?这取决于编译器和CPU体系结构。在VS2012下编译时,我发现一个简单的循环在我的i3处理器上运行速度快了60%,但是在Raspberry Pi中的ARM11芯片上,使用GCC编译时,我只得到了20%的改进。

“与%运算符相比,这段代码涉及while循环的效率足够吗?”您告诉我们,您就是使用该程序的人。感觉慢吗?你能注意到吗?你有没有分析过,发现这个速度很慢?这取决于大小。如果
b=100000000
c=3
。这需要一段时间…你能告诉目标CPU和编译器吗?如果没有这一点,就不可能比较任何方法。您可能会尝试分析不同的版本,以了解什么更适合您的环境。你试过了吗?我认为在大多数情况下,%以上是相当有效的。既然您有两种版本的实现方法,为什么不在您的环境中对这两种方法进行分析,并为自己获得结果,这将比任何答案都更具决定性,这将对您的环境做出明智的猜测或近似值。+1用于在担心优化之前正确完成程序。许多失败项目的原因。完成后,引号更好:我们应该忘记小效率,比如说97%的时间:过早优化是万恶之源,否则答案是好的。当正确的操作数是两个常数的幂时,任何体面的编译器都会为您进行同样的优化。位移位/掩蔽技巧是早期编译器优化失败的遗留问题,应该不再需要了
int32_t signedVal = static_cast< int32_t >( val - dec );
val = signedVal + ( modulo & ( signedVal >> 31 ) );