我试图对一个整数进行mod以获得一个数组位置,这样它就会循环。做i % arrayLength适用于正数,但对于负数就完全出错了。

 4 % 3 == 1
 3 % 3 == 0
 2 % 3 == 2
 1 % 3 == 1
 0 % 3 == 0
-1 % 3 == -1
-2 % 3 == -2
-3 % 3 == 0
-4 % 3 == -1

我需要一个实现

int GetArrayIndex(int i, int arrayLength)

这样

GetArrayIndex( 4, 3) == 1
GetArrayIndex( 3, 3) == 0
GetArrayIndex( 2, 3) == 2
GetArrayIndex( 1, 3) == 1
GetArrayIndex( 0, 3) == 0
GetArrayIndex(-1, 3) == 2
GetArrayIndex(-2, 3) == 1
GetArrayIndex(-3, 3) == 0
GetArrayIndex(-4, 3) == 2

我以前也这么做过,但不知为何,今天我的脑子都要融化了:(


当前回答

增加一些理解。

根据欧几里得的定义,取模的结果必须总是正的。

Ex:

 int n = 5;
 int x = -3;

 int mod(int n, int x)
 {
     return ((n%x)+x)%x;
 }

输出:

 -1

其他回答

ShreevatsaR的第二个答案是:

int mod(int x, int m) {
    int r = x % m;
    return r < 0 ? r + m : r;
}

可以在新版本的c#中使用var模式和switch表达式作为一行程序来编写:

int mod(int x, int m) => (x % m) switch 
{ 
    < 0 and var r => r + m, var r => r 
}

对于更注重性能的开发人员

uint wrap(int k, int n) ((uint)k)%n

一个小的性能比较

Modulo: 00:00:07.2661827 ((n%x)+x)%x)
Cast:   00:00:03.2202334 ((uint)k)%n
If:     00:00:13.5378989 ((k %= n) < 0) ? k+n : k

至于浇注到uint的性能成本在这里看一下

单行实现只使用%一次:

int mod(int k, int n) {  return ((k %= n) < 0) ? k+n : k;  }

我喜欢Peter N Lewis在这篇文章中提出的技巧:“如果N有一个有限的范围,那么你可以通过添加一个已知的常数倍数(除数)来得到你想要的结果,这个倍数大于最小值的绝对值。”

如果我有一个以度数为单位的值d,我想取

d % 180f

我想避免d为负时的问题,那么我就这样做:

(d + 720f) % 180f

这里假设d可能是负数,但已知它永远不会大于-720。

请注意,c#和c++的%运算符实际上不是模数,而是余数。在你的例子中,求模的公式是:

float nfmod(float a,float b)
{
    return a - b * floor(a / b);
}

你必须用c#(或c++)重新编码,但这是你得到模数而不是余数的方法。