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;