web-dev-qa-db-fra.com

Opération Modulo avec des nombres négatifs

Dans un programme c j'essayais les opérations ci-dessous (juste pour vérifier le comportement)

 x = 5 % (-3);
 y = (-5) % (3);
 z = (-5) % (-3); 

printf("%d ,%d ,%d", x, y, z); 

m'a donné la sortie en tant que (2, -2 , -2) dans gcc. Je m'attendais à un résultat positif à chaque fois. Un module peut-il être négatif? Quelqu'un peut-il expliquer ce comportement?

151
Alva

C99 requiert que lorsque a/b soit représentable:

(a/b) * b+a%b doit être égal à a

Cela a du sens, logiquement. Droite?

Voyons ce que cela mène à:


Exemple A. 5/(-3) est -1

=> (-1) * (-3)+5%(-3) = 5

Cela ne peut se produire que si 5%(-3) est 2.


Exemple B. (-5)/3 est -1

=> (-1) * 3+(-5)%3 = -5

Cela ne peut se produire que si (-5)%3 est -2

132
ArjunShankar

L'opérateur % en C n'est pas l'opérateur modulo mais l'opérateur reste - .

Les opérateurs Modulo et Restly diffèrent en ce qui concerne les valeurs négatives. 

Avec un opérateur restant, le signe du résultat est identique à celui du dividende, tandis que pour un opérateur modulo, le signe du résultat est identique à celui du diviseur.

C définit l'opération % pour a % b comme suit:

  a == (a / b * b) + a % b

avec / la division entière avec troncature vers 0. C'est la troncature faite vers 0 (et non vers l'inifinité négative) qui définit le % en tant qu'opérateur de reste plutôt qu'en tant qu'opérateur modulo.

111
ouah

Basé sur la spécification C99: a = (a / b) * b + a % b

Nous pouvons écrire une fonction pour calculer (a % b) = a - (a / b) * b!

int remainder(int a, int b)
{
    return a - (a / b) * b;
}

Pour le fonctionnement modulo, on peut avoir la fonction suivante (en supposant que b> 0)

int mod(int a, int b)
{
    int r = a % b;
    return r < 0 ? r + b : r;
}

Ma conclusion est que (a% b) dans C est un opérateur restant et non un opérateur modulo.

56
dewang

Je ne pense pas qu'il ne soit pas nécessaire de vérifier si le nombre est négatif.

Une fonction simple pour trouver le modulo positif serait ceci -

int modulo(int x,int N){
    return (x % N + N) %N;
}

En supposant que N soit positif, cela fonctionnera pour à la fois positif et négatif les valeurs de x.

P.S. également comme l'a souligné @chux, Si votre x et votre N peuvent atteindre quelque chose comme INT_MAX-1 et INT_MAX respectivement, il suffit de remplacer int par long long int.

Et s’ils franchissent également les limites de long long (c’est-à-dire près de LLONG_MAX), vous devrez alors traiter les cas positifs et négatifs séparément, comme décrit dans d’autres réponses ici.

39
Udayraj Deshmukh

Les autres réponses ont été expliquées dans C99 ou plus tard, division des nombres entiers impliquant des opérandes négatifs toujours tronquée vers zéro

Notez que, dans C89 , le résultat arrondi à la hausse ou à la baisse est défini par l'implémentation. Étant donné que (a/b) * b + a%b est égal à a dans toutes les normes, le résultat de % impliquant des opérandes négatifs est également défini dans C89.

7
Yu Hao

Un module peut-il être négatif? 

% est l'opérateur reste , le reste après la division, pas après Euclidean_division . Depuis C99, le résultat peut être 0, négatif ou positif.

 // a % b
 7 %  3 -->  1  
 7 % -3 -->  1  
-7 %  3 --> -1  
-7 % -3 --> -1  

Je m'attendais à un résultat positif à chaque fois.

Pour effectuer un modulo euclidien bien défini à chaque fois que a/b est défini, a,b est composé de n'importe quel signe et le résultat n'est jamais négatif:

int modulo_Euclidean(int a, int b) {
  int m = a % b;
  if (m < 0) {
    // m += (b < 0) ? -b : b; // avoid this form: it is UB when b == INT_MIN
    m = (b < 0) ? m - b : m + b;
  }
  return m;
}

modulo_Euclidean( 7,  3) -->  1  
modulo_Euclidean( 7, -3) -->  1  
modulo_Euclidean(-7,  3) -->  2  
modulo_Euclidean(-7, -3) -->  2   
3
chux

Le résultat de l'opération Modulo dépend du signe du numérateur. Vous obtenez donc -2 pour y et z.

Voici la référence

http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_14.html

Division entière

Cette section décrit les fonctions permettant d'effectuer une division entière . Ces fonctions sont redondantes dans la bibliothèque GNU C, car dans GNU C, le fichier L'opérateur '/' arrondit toujours vers zéro. Mais dans d'autres C implémentations, '/' peut être arrondi différemment par des arguments négatifs . div et ldiv sont utiles car ils précisent comment arrondir le quotient: vers zéro. Le reste a le même signe que le numérateur.

3
Kartik Anand

En mathématiques, d'où découlent ces conventions, rien ne permet d'affirmer que l'arithmétique modulo devrait donner un résultat positif.

Par exemple. 

1 mod 5 = 1, mais il peut aussi être égal à -4. Autrement dit, 1/5 donne le reste 1 de 0 ou -4 de 5. (Les deux facteurs de 5)

De même, - 1 mod 5 = -1, mais il peut aussi être égal à 4. Autrement dit, -1/5 donne un reste -1 de 0 ou 4 de -5. (Les deux facteurs de 5)

Pour en savoir plus, consultez Cours d'équivalence en mathématiques. 

2
DarkPurple141

Conformément à norme C99 , section 6.5.5 Opérateurs multiplicatifs, les éléments suivants sont requis:

(a / b) * b + a % b = a

Conclusion

Le signe du résultat d'une opération restante, selon le C99, est le même que celui du dividende.

Voyons quelques exemples (dividend / divisor):

Quand seul le dividende est négatif

(-3 / 2) * 2  +  -3 % 2 = -3

(-3 / 2) * 2 = -2

(-3 % 2) must be -1

Quand seul diviseur est négatif

(3 / -2) * -2  +  3 % -2 = 3

(3 / -2) * -2 = 2

(3 % -2) must be 1

Lorsque le diviseur et le dividende sont tous deux négatifs

(-3 / -2) * -2  +  -3 % -2 = -3

(-3 / -2) * -2 = -2

(-3 % -2) must be -1

6.5.5 Opérateurs multiplicatifs

Syntaxe

  1. expression multiplicative:
    • cast-expression
    • multiplicative-expression * cast-expression
    • multiplicative-expression / cast-expression
    • multiplicative-expression % cast-expression

Contraintes

  1. Chacun des opérandes doit avoir un type arithmétique. Le les opérandes de l'opérateur % doivent avoir un type entier.

Sémantique

  1. Les conversions arithmétiques habituelles sont effectuées sur le des opérandes.

  2. Le résultat de l'opérateur binaire * est le produit de les opérandes.

  3. Le résultat de l'opérateur / est le quotient de la division du premier opérande par le second; la Le résultat de l'opérateur % est le reste. À la fois opérations, si la valeur du deuxième opérande est zéro, le comportement est indéfini.

  4. Lorsque des entiers sont divisés, le résultat de l'opérateur / est le quotient algébrique avec une partie fractionnaire mis au rebut [1]. Si le quotient a/b est représentable, l'expression (a/b)*b + a%b doit être égale à a.

[1]: Ceci est souvent appelé "troncature vers zéro".

1

L'opérateur Modulus donne le reste . L'opérateur Modulus en c prend généralement le signe du numérateur

  1. x = 5% (-3) - ici le numérateur est positif et donne donc 2
  2. y = (-5)% (3) - ici le numérateur est négatif d'où il résulte -2
  3. z = (-5)% (-3) - ici le numérateur est négatif d'où il résulte -2

De plus, l'opérateur modulus (reste) ne peut être utilisé qu'avec un type entier et ne peut pas être utilisé avec une virgule flottante.

1
Kavya

L'opérateur modulo est semblable à l'opérateur mod lorsque le nombre est positif, mais différent si le nombre est négatif.

Plusieurs fois dans les problèmes on nous demande de donner la réponse modulo 10 ^ 9 + 7.

Laissez la réponse (avant d'utiliser modulo) être notée par «a».

Simple règle simple-

si a est positif , alors un modulo 10 ^ 9 + 7 =a% (10 ^ 9 + 7)

si a est négatif , alors un modulo 10 ^ 9 + 7 =(a% (10 ^ 9 + 7)) + (10 ^ 9 + 7)

Si, dans de tels problèmes, nous trouvons que n'importe quelle étape de la boucle peut calculer une valeur en dehors de la plage d'entiers (si nous utilisons des entiers), nous pouvons utiliser l'opérateur modulo dans cette étape elle-même. La réponse finale sera comme si nous n’avions utilisé l’opérateur modulo qu’une seule fois.

C'est parce que- (a * b)% c = ((a% c) (b% c))% c Même chose pour l'addition et la soustraction.

0
Akash Bhalotia

Je pense qu'il est plus utile de penser à mod tel qu'il est défini dans l'arithmétique abstraite; non pas comme une opération, mais comme une classe d'arithmétique différente, avec des éléments et des opérateurs différents. Cela signifie que l'addition dans mod 3 n'est pas la même que l'addition "normale"; C'est; addition entière.

Alors quand tu fais:

5 % -3

Vous essayez de mapper le entier 5 à un élément de l'ensemble mod -3. Ce sont les éléments de mod -3:

{ 0, -2, -1 }

Alors:

0 => 0, 1 => -2, 2 => -1, 3 => 0, 4 => -2, 5 => -1

Supposons que vous deviez rester éveillé pour une raison quelconque 30 heures. Combien d’heures restera-t-il de cette journée? 30 mod -24.

Mais ce que C implémente n’est pas mod, c’est un reste. Quoi qu'il en soit, le fait est qu'il est logique de renvoyer des négatifs.

0
FelipeC