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

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


当前回答

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

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

d % 180f

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

(d + 720f) % 180f

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

其他回答

我喜欢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++)重新编码,但这是你得到模数而不是余数的方法。

如果你的除数是正数,这里所有的答案都很有效,但它并不完全。下面是我的实现,它总是在[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可以是任何数字类型)

mod函数有很多实现,我认为有必要列出所有实现——至少根据维基百科,我相信还有更多。

// Important to be able to use `MathF`.
using System;

public static class MathFUtils {
    public static class Mod {
        public static float Trunc(float a, float b) =>
            a - b * ((int)(a / b));

        public static float Round(float a, float b) =>
            a - b * MathF.Round(a / b);

        public static float Floor(float a, float b) =>
            a - b * MathF.Floor(a / b);

        public static float Ceil(float a, float b) =>
            a - b * MathF.Ceiling(a / b);

        public static float Euclidean(float a, float b) =>
            a - MathF.Abs(b) * MathF.Floor(a / MathF.Abs(b));
    }
}

根据维基百科(以及我的经验),坚持欧几里得。它在数学和概率性质方面是最有用的。如果您需要Trunc,那么我相信%可以做到这一点。

此外,对于那些可能对它们各自做什么以及如何做感到困惑的人,我强烈建议阅读维基百科的文章(即使很难)并查看每个表示的图像。

当然,这些不一定是性能最好的,但它们确实有效。如果你关心性能,我建议你找一个本地的c#之神,或者在他们经过我们的尘世时问他。

增加一些理解。

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

Ex:

 int n = 5;
 int x = -3;

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

输出:

 -1