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

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


当前回答

如果你的除数是正数,这里所有的答案都很有效,但它并不完全。下面是我的实现,它总是在[0,b)的范围内返回,这样输出的符号与除数的符号相同,允许负除数作为输出范围的端点。

重新启动2 返回1 重新启动-1 重新启动-2

    /// <summary>
    /// Performs a canonical Modulus operation, where the output is on the range [0, b).
    /// </summary>
    public static real_t PosMod(real_t a, real_t b)
    {
        real_t c = a % b;
        if ((c < 0 && b > 0) || (c > 0 && b < 0)) 
        {
            c += b;
        }
        return c;
    }

(real_t可以是任何数字类型)

其他回答

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 
}

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

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

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

比较前两个答案

(x%m + m)%m;

and

int r = x%m;
return r<0 ? r+m : r;

实际上没有人提到第一个可能会抛出OverflowException,而第二个则不会。更糟糕的是,在默认的未选中上下文的情况下,第一个答案可能返回错误的答案(参见mod(int)。MaxValue - 1, int.MaxValue)为例)。所以第二个答案不仅看起来更快,而且更正确。

我总是使用我自己的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的模的值。

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

用法:

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

实现:

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