J'essaie de modifier un entier pour obtenir une position sur un tableau afin qu'il soit bouclé. Faire i %
arrayLength
fonctionne bien pour les nombres positifs, mais pour les nombres négatifs, tout va mal.
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
donc j'ai besoin d'une implémentation de
int GetArrayIndex(int i, int arrayLength)
tel que
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
Je l'ai déjà fait auparavant, mais pour une raison quelconque, mon cerveau est en train de fondre aujourd'hui :(
J'utilise toujours ma propre fonction mod
, définie par
int mod(int x, int m) {
return (x%m + m)%m;
}
Bien sûr, si vous êtes dérangé par le fait d'avoir deux appels à l'opération modulus, vous pouvez l'écrire comme suit:
int mod(int x, int m) {
int r = x%m;
return r<0 ? r+m : r;
}
ou leurs variantes.
Cela fonctionne parce que "x% m" est toujours dans la plage [-m + 1, m-1]. Donc, s'il est négatif, ajouter m le placera dans la plage positive sans modifier sa valeur modulo m.
Veuillez noter que l'opérateur% C # et C++ n'est en réalité PAS un modulo, c'est le reste. La formule de modulo que vous souhaitez, dans votre cas, est la suivante:
float nfmod(float a,float b)
{
return a - b * floor(a / b);
}
Vous devez recoder ceci en C # (ou C++), mais c'est ainsi que vous obtenez modulo et non un reste.
Implémentation sur une seule ligne en utilisant %
une seule fois:
int mod(int k, int n) { return ((k %= n) < 0) ? k+n : k; }
La réponse de ShreevatsaR ne fonctionnera pas dans tous les cas, même si vous ajoutez "if (m <0) m = -m;", si vous tenez compte des dividendes/diviseurs négatifs.
Par exemple, -12 mod -10 sera 8, et il devrait être -2.
L'implémentation suivante fonctionnera pour les diviseurs/diviseurs positifs et négatifs et sera conforme à d'autres implémentations (notamment Java, Python, Ruby, Scala, Scheme, Javascript et la calculatrice de 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;
}
}
Suite de test utilisant 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 };
}
}
Pour les développeurs plus conscients des performances
uint wrap(int k, int n) ((uint)k)%n
Une petite comparaison de performance
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
En ce qui concerne les coûts de performance de la fonte à uint jeter un oeil ici
Ajouter un peu de compréhension.
Par Définition euclidienne le résultat du mod doit toujours être positif.
Ex:
int n = 5;
int x = -3;
int mod(int n, int x)
{
return ((n%x)+x)%x;
}
Sortie:
-1
Ajoutez simplement votre module (arrayLength) au résultat négatif de% et tout ira bien.
Comparer deux réponses prédominantes
(x%m + m)%m;
et
int r = x%m;
return r<0 ? r+m : r;
Personne n’a en fait mentionné le fait que le premier peut lancer une OverflowException
alors que le second ne le fera pas. Pire encore, avec un contexte non contrôlé par défaut, la première réponse peut renvoyer la mauvaise réponse (voir mod(int.MaxValue - 1, int.MaxValue)
par exemple). La deuxième réponse semble donc non seulement être plus rapide, mais aussi plus correcte.
J'aime le tour présenté par Peter N Lewis sur ce fil de discussion : "Si n a une portée limitée, vous pouvez obtenir le résultat souhaité en ajoutant simplement un multiple constant connu valeur absolue du minimum. "
Donc, si j’ai une valeur d en degrés et que je veux prendre
d % 180f
et je veux éviter les problèmes si d est négatif, alors je fais simplement ceci:
(d + 720f) % 180f
Cela suppose que bien que d puisse être négatif, il est connu qu'il ne sera jamais plus négatif que -720.
Toutes les réponses ici fonctionnent très bien si votre diviseur est positif, mais ce n'est pas tout à fait complet. Voici mon implémentation qui renvoie toujours sur une plage de [0, b)
, telle que le signe de la sortie soit identique à celui du diviseur, ce qui permet d'utiliser des diviseurs négatifs comme point final de la plage de sortie.
PosMod(5, 3)
renvoie 2
PosMod(-5, 3)
renvoie 1
PosMod(5, -3)
renvoie -1
PosMod(-5, -3)
renvoie -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;
}
(où real_t
peut être n'importe quel type de nombre)