负数的Mod正在融化我的大脑

时间:2009-07-04 20:22:44

标签: c# math modulo

我正在尝试修改一个整数来获取一个数组位置,以便它循环。执行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

我以前做过这个,但由于某种原因,它今天融化了我的大脑:(

14 个答案:

答案 0 :(得分:252)

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

答案 1 :(得分:70)

请注意,C#和C ++的%运算符实际上不是模数,它是余数。在您的情况下,您想要的模数公式为:

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

你必须在C#(或C ++)中重新编码,但这是你获得模数而不是余数的方式。

答案 2 :(得分:13)

仅使用%进行一次单行实施:

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

答案 3 :(得分:5)

增加一些理解。

Euclidean definition,mod结果必须始终为正。

例如:

 int n = 5;
 int x = -3;

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

输出:

 -1

答案 4 :(得分:5)

ShreevatsaR的答案不适用于所有情况,即使您添加“if(m <0)m = -m;”,如果您考虑负股息/除数。

例如,-12 mod -10将为8,它应为-2。

以下实现适用于正面和负面的红利/除数,并符合其他实现(即Java,Python,Ruby,Scala,Scheme,Javascript和Google的计算器):

internal static class IntExtensions
{
    internal static int Mod(this int a, int n)
    {
        if (n == 0)
            throw new ArgumentOutOfRangeException("n", "(a mod 0) is undefined.");

        //puts a in the [-n+1, n-1] range using the remainder operator
        int remainder = a%n;

        //if the remainder is less than zero, add n to put it in the [0, n-1] range if n is positive
        //if the remainder is greater than zero, add n to put it in the [n-1, 0] range if n is negative
        if ((n > 0 && remainder < 0) ||
            (n < 0 && remainder > 0))
            return remainder + n;
        return remainder;
    }
}

使用xUnit的测试套件:

    [Theory]
    [PropertyData("GetTestData")]
    public void Mod_ReturnsCorrectModulo(int dividend, int divisor, int expectedMod)
    {
        Assert.Equal(expectedMod, dividend.Mod(divisor));
    }

    [Fact]
    public void Mod_ThrowsException_IfDivisorIsZero()
    {
        Assert.Throws<ArgumentOutOfRangeException>(() => 1.Mod(0));
    }

    public static IEnumerable<object[]> GetTestData
    {
        get
        {
            yield return new object[] {1, 1, 0};
            yield return new object[] {0, 1, 0};
            yield return new object[] {2, 10, 2};
            yield return new object[] {12, 10, 2};
            yield return new object[] {22, 10, 2};
            yield return new object[] {-2, 10, 8};
            yield return new object[] {-12, 10, 8};
            yield return new object[] {-22, 10, 8};
            yield return new object[] { 2, -10, -8 };
            yield return new object[] { 12, -10, -8 };
            yield return new object[] { 22, -10, -8 };
            yield return new object[] { -2, -10, -2 };
            yield return new object[] { -12, -10, -2 };
            yield return new object[] { -22, -10, -2 };
        }
    }

答案 5 :(得分:5)

了解更多关注性能的开发者

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的性能成本看看here

答案 6 :(得分:4)

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

答案 7 :(得分:2)

我喜欢Peter N Lewis在this thread上提出的技巧:“如果n的范围有限,那么你只需添加一个已知的[除数]的常数倍数即可得到你想要的结果那个最小值的绝对值。“

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

d % 180f

如果 d 是否定的话我想避免这些问题,那么我只是这样做:

(d + 720f) % 180f

这假设虽然 d 可能是负数,但已知它永远不会比-720更负。

答案 8 :(得分:2)

比较两个主要答案

(x%m + m)%m;

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

实际上没有人提到第一个可能抛出OverflowException而第二个则不会抛出的事实。更糟糕的是,如果使用默认的未经检查的上下文,则第一个答案可能会返回错误的答案(例如,请参见mod(int.MaxValue - 1, int.MaxValue))。因此,第二个答案似乎不仅更快,而且更正确。

答案 9 :(得分:1)

您期望的行为与c#中%运算符的已记录行为相反-可能是因为您期望它以一种您更习惯的另一种语言工作的方式工作。 {#3}在c#状态下(强调我的意思):

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

您可以通过一个额外的步骤来计算所需的值:

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

答案 10 :(得分:1)

dcastro 答案的单行实现(与其他语言最兼容):

int Mod(int a, int n)
{
    return (((a %= n) < 0) && n > 0) || (a > 0 && n < 0) ? a + n : a;
}

如果您想继续使用%运算符(不能在C#中重载本机运算符):

public class IntM
{
    private int _value;

    private IntM(int value)
    {
        _value = value;
    }

    private static int Mod(int a, int n)
    {
        return (((a %= n) < 0) && n > 0) || (a > 0 && n < 0) ? a + n : a;
    }

    public static implicit operator int(IntM i) => i._value;
    public static implicit operator IntM(int i) => new IntM(i);
    public static int operator %(IntM a, int n) => Mod(a, n);
    public static int operator %(int a, IntM n) => Mod(a, n);
}

用例,两者均可:

int r = (IntM)a % n;

// Or
int r = a % n(IntM);

答案 11 :(得分:0)

如果您的除数为正,则此处的所有答案都非常有用,但还不够完整。这是我的实现,总是在[0, b)的范围内返回,以使输出的符号与除数的符号相同,从而允许负除数作为输出范围的端点。

PosMod(5, 3)返回2
PosMod(-5, 3)返回1
PosMod(5, -3)返回-1
PosMod(-5, -3)返回-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可以是任何数字类型)

答案 12 :(得分:0)

我观察到一件事。它在python中仅给出正数 print 4%3 print 3%3 print 3%3 print 2%3 print 1%3 print 0%3 print -1%3 print -2%3 print -3%3 print -4%3 -这给了我
1 0 0 2 1 0 2 1 0 2 但是在C语言中,它给出了@gormenghastly给出的答案

#include<stdio.h>

int main()
{
printf("\n %d",3 % 3);
printf("\n %d",4 % 3);
printf("\n %d",3 % 3);
printf("\n %d",2 % 3);
printf("\n %d",1 % 3); 
printf("\n %d", 0 % 3);
printf("\n %d",-1 % 3);
printf("\n %d",-2 % 3);
printf("\n %d",-3 % 3);
printf("\n %d",-4 % 3);

return 0;
}

我正在从python输出中看到的百分比来定义%,但是现在我也很困惑

答案 13 :(得分:0)

这是我的一个正整数衬里,基于 this answer

用法:

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

实现:

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