C# 负数模正在融化我的大脑

C# 负数模正在融化我的大脑,c#,math,modulo,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 Ge

我试图修改一个整数以获得数组位置,这样它将循环。做
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

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

只要把你的模数(arrayLength)加上%的负结果,你就没事了。

我总是使用我自己的
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;
}
intmod(intx,intm){
int r=x%m;
返回r请注意,C#和C++的%运算符实际上不是模,而是余数。在本例中,您需要的模公式是:

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

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

我喜欢Peter N Lewis提出的技巧:“如果N的范围有限,那么你只需将已知的常数乘以大于最小值绝对值的[除数]就可以得到你想要的结果。”

如果我有一个d值,单位是度,我想取

d % 180f
如果d为负,我想避免这些问题,那么我只需要这样做:

(d + 720f) % 180f

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

增加了一些理解

到mod时,结果必须始终为正

例:

输出:

 -1

ShreevatsaR的答案并不适用于所有情况,即使您只添加一次“if(m使用
%
的单行实现:

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

为更注重性能的开发人员

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
模:00:00:07.2661827((n%x)+x)%x) 演员:00:00:03.2202334((单位)k)%n 如果:00:00:13.5378989((k%=n)<0)?k+n:k

至于cast to uint的性能成本,请查看比较两个主要答案的情况

(x%m + m)%m;

int r=x%m;

return r如果除数为正,那么这里的所有答案都很有效,但它并不完全。下面是我的实现,它总是在
[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;
    }
//
///执行标准模运算,其中输出在范围[0,b]上。
/// 
公共静态实时PosMod(实时a、实时b)
{
实际成本=a%b;
if((c<0&&b>0)|(c>0&&b<0))
{
c+=b;
}
返回c;
}

(其中,
real\u t
可以是任何数字类型)

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

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

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

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

dcastro答案的单行实现(最符合其他语言):

intmod(inta,intn)
{
回报率((a%=n<0)和&n>0)|(a>0和&n<0)?a+n:a;
}
如果希望继续使用
%
运算符(不能在C#中重载本机运算符),请执行以下操作:

公共类IntM
{
私有整数值;
私有IntM(int值)
{
_价值=价值;
}
私有静态整数模(整数a,整数n)
{
回报率((a%=n<0)和&n>0)|(a>0和&n<0)?a+n:a;
}
公共静态隐式运算符int(IntM i)=>i.\u值;
公共静态隐式运算符IntM(inti)=>新的IntM(i);
公共静态整数运算符%(整数a,整数n)=>Mod(a,n);
公共静态整数运算符%(整数a,整数n)=>Mod(a,n);
}
在用例中,这两种方法都有效:

intr=(IntM)a%n;
//或
intr=a%n(IntM);

这是我的正整数的一行,基于:

用法:

(-7).Mod(3); // returns 2
实施:

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

注意:为了完整的数论完整性,您可能需要在顶部添加一行,表示“如果”(如果要检查m的值,还应排除零。@billpg:mod不是为m=0定义的,因此函数在这种情况下实际上不需要做任何事情。IMHO,检查这一点是调用方的责任。(任何人都不应该想要mod 0。)OTOH,mod是为负m定义的,因此我建议如果函数可以用负m调用,则修复代码中的错误。无论如何,应该在哪里进行错误检查/处理是一个长期存在的问题:p@RuudLenders:否。如果x=-5且m=2,则
r=x%m
-1
,之后
r+m
1
循环是不需要的,关键是(正如我在回答中所写的),
x%m
始终严格大于
-m
,因此您最多需要添加一次
m
,以使其为正。+1.我不在乎任何一种语言对负模做了什么—“最小非负剩余”显示出数学规律性并消除任何歧义。“请注意,C++的%运算符实际上不是一个模,而是一个余数。”谢谢,这很有意义,现在总是想知道为什么它从来没有正确地处理负数。“请注意,C++的%运算符实际上不是一个模,
    /// <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;
    }
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;