有没有更有效的方法来计算正模? < Modulo > C和C++没有以数学正确的方式表现,因为它在执行负数模时返回否定的结果。在做了一些研究之后,实现正确行为的经典方法似乎是: positive modulo(int i, int n) { return ( ( (i % n) + n ) % n ); }

有没有更有效的方法来计算正模? < Modulo > C和C++没有以数学正确的方式表现,因为它在执行负数模时返回否定的结果。在做了一些研究之后,实现正确行为的经典方法似乎是: positive modulo(int i, int n) { return ( ( (i % n) + n ) % n ); },c++,modulo,performance,C++,Modulo,Performance,考虑到模的计算成本很高,有没有更有效的方法来计算任意数的正模?我已经看到了2的幂的解决方案,但我需要一些通用的方法?这可能会更慢或更快,取决于编译器、优化级别和体系结构: static inline int modulo(int i, int n) { const int k = i % n; return k < 0 ? k + n : k; } 其速度较慢的原因是条件操作可能引入分支,有时分支速度较慢。这可能较慢或较快,具体取决于编译器、优化级别和体系结构: static i

考虑到模的计算成本很高,有没有更有效的方法来计算任意数的正模?我已经看到了2的幂的解决方案,但我需要一些通用的方法?

这可能会更慢或更快,取决于编译器、优化级别和体系结构:

static inline int modulo(int i, int n) {
  const int k = i % n;
  return k < 0 ? k + n : k;
}

其速度较慢的原因是条件操作可能引入分支,有时分支速度较慢。

这可能较慢或较快,具体取决于编译器、优化级别和体系结构:

static inline int modulo(int i, int n) {
  const int k = i % n;
  return k < 0 ? k + n : k;
}

它之所以较慢,是因为条件运算可能会引入分支,有时分支会较慢。

公式中的第二个模只需要在第一个模为正的情况下再次减去n。因此,仅有条件地添加n至少应该同样有效:


公式中的第二个模只有在模是正定的情况下才需要再次减去n。因此,仅有条件地添加n至少应该同样有效:

中的解决方案可能是最好的解决方案。它通常编译成无分支代码,但即使分支速度变慢,它也可能比除法更快。但万一你真的需要避免分支

inline int modulo(int i, int n)
{
    int k = i % n;
    int a = -(k < 0);  // assuming 2's complement
    // or int a = ((k < 0) << (INT_SIZE - 1)) >> (INT_SIZE - 1); if your system doesn't use 2's complement
    return k + n & a;
}
中的解决方案可能是最好的解决方案。它通常编译成无分支代码,但即使分支速度变慢,它也可能比除法更快。但万一你真的需要避免分支

inline int modulo(int i, int n)
{
    int k = i % n;
    int a = -(k < 0);  // assuming 2's complement
    // or int a = ((k < 0) << (INT_SIZE - 1)) >> (INT_SIZE - 1); if your system doesn't use 2's complement
    return k + n & a;
}


只是ABS值第一个?@ RICHARJ.R.Ssiii:做了错误的事情。C和C++中的加法在数学上是不正确的,因为在执行非常大的数字时,它会产生否定的结果。对不起,我无法抗拒。编程中的大多数操作在数学上都是不正确的。模的数学结果应该始终为正,并且应该是你需要加多少才能达到除数d最接近的n的倍数。数学模与除数有相同的符号,或者零向负无穷大舍入。C/C++模与红利或零舍入的符号相同,只是ABS值第一个。@ RICHARJ.R.SRSIII:做错了。C和C++中的加法在数学上是不正确的,因为在执行非常大的数时,它会返回否定的结果。对不起,我无法抗拒。编程中的大多数操作在数学上都是不正确的。模的数学结果应该始终为正,并且应该是你需要加多少才能达到除数d最接近的n的倍数。数学模与除数有相同的符号,或者零向负无穷大舍入。C/C++模与分红或零舍入的符号相同,分支是慢的,但目前大多数没有硬件除数的设备大多是嵌入式系统,其中分支不影响性能,因为它没有超标量或其他一些特性,所以我的系统编译成MOVL。%edi、%eax cltd idivl%esi addl%edx、%esi movl%edx、%eax testl%edx、%edx cmovs%esi、%eax没有分支,而是使用一个应该快速的条件移动。逻辑相当于返回i%n+i<0*n,假设n>0@MattMcNabb:使用现代编译器,iIt的意思是澄清一下:现在我们相信编译器能够为简单的算术序列生成最好的编译代码。分支速度很慢,但今天大多数没有硬件除数的设备大多是嵌入式系统,分支与否对性能没有多大影响,因为它没有超标量或其他功能在我的系统上,它编译为movl%edi、%eax cltd idivl%esi addl%edx、%esi movl%edx、%eax testl%edx、%edx cmovs%esi、%eax,它没有分支,它改为使用一个应该快速的条件移动。该逻辑相当于返回i%n+i<0*n,假设n>0。@MattMcNabb:对于现代编译器,iIt意味着一个澄清:现在我们相信编译器能够为简单算术序列生成最佳编译代码。我怎么知道是否使用了2的补码?大多数现代系统使用2的补码a=-k<0;无论表示形式如何,它都会将a设置为0或-1。要使代码可移植,请将n&a更改为n*-a。希望编译器在这两种情况下都能生成最佳的代码。我怎么知道2的comp
使用liment?大多数现代系统使用2的补码a=-k<0;无论表示形式如何,它都会将a设置为0或-1。要使代码可移植,请将n&a更改为n*-a。希望编译器在这两种情况下都能生成最佳代码。