我试图对一个整数进行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

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


当前回答

我总是使用我自己的mod函数,定义为

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

当然,如果你不介意对模运算进行两次调用,你可以把它写成

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

或其变体。

它起作用的原因是“x%m”总是在[-m+1, m-1]的范围内。所以如果它是负的,加上m就会使它在正范围内而不改变它对m的模的值。

其他回答

您期望的行为与c#中%操作符的记录行为相反——可能是因为您期望它以一种在您更习惯的另一种语言中工作的方式工作。c#状态的文档(重点是我的):

对于整数类型的操作数,a % b的结果是a - (a / b) * b产生的值。非零余数的符号与左操作数的符号相同

你想要的值可以通过一个额外的步骤来计算:

int GetArrayIndex(int i, int arrayLength){
    int mod = i % arrayLength;
    return (mod>=0) : mod ? mod + arrayLength;
}

这是我对正整数的一行代码,基于这个答案:

用法:

(-7).Mod(3); // returns 2

实现:

static int Mod(this int a, int n) => (((a %= n) < 0) ? n : 0) + a;

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

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的性能成本在这里看一下

只需将您的模量(arrayLength)添加到%的负结果,就可以了。

增加一些理解。

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

Ex:

 int n = 5;
 int x = -3;

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

输出:

 -1