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 comme (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?
Réponses:
C99 exige que quand
a/b
est représentable:(a/b) * b
+a%b
est égal àa
Cela a du sens, logiquement. Droite?
Voyons à quoi cela mène:
L'exemple A.
5/(-3)
est-1
=>
(-1) * (-3)
+5%(-3)
=5
Cela ne peut se produire que si
5%(-3)
est 2.L'exemple B.
(-5)/3
est-1
=>
(-1) * 3
+(-5)%3
=-5
Cela ne peut se produire si
(-5)%3
est-2
la source
-5/3
est-2
et le mod devient 1. En bref: un module a un signe qui suit le signe du dividende (tronquer), l'autre module a un signe qui suit le signe diviseur (Knuth).L'
%
opérateur en C n'est pas l' opérateur modulo mais le reste opérateur .Les opérateurs modulo et reste diffèrent par rapport aux valeurs négatives.
Avec un opérateur de reste, le signe du résultat est le même que le signe du dividende tandis qu'avec un opérateur modulo le signe du résultat est le même que le diviseur.
C définit l'
%
opération poura % b
comme:avec
/
la division entière avec troncature vers0
. C'est la troncature qui est faite vers0
(et non vers l'inifinité négative) qui définit le%
comme un opérateur de reste plutôt qu'un opérateur modulo.la source
remainder
etmodulo
dans Schemerem
etmod
dans Haskell). Les spécifications de ces opérateurs diffèrent sur ces langages sur la manière dont la division est effectuée: troncature vers 0 ou vers l'infini négatif. Soit dit en passant la norme C appelle jamais%
l' opérateur modulo , ils nomment simplement l' opérateur% .remainder
fonction en C, qui implémente le reste IEEE avec une sémantique arrondie vers la plus proche dans la divisionBasé sur la spécification C99:
a == (a / b) * b + a % b
On peut écrire une fonction à calculer
(a % b) == a - (a / b) * b
!int remainder(int a, int b) { return a - (a / b) * b; }
Pour le fonctionnement modulo, nous pouvons avoir la fonction suivante (en supposant
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 une opération de reste et PAS une opération modulo.la source
b
est négatif (et en fait pourr
et lesb
deux négatifs, cela donne des résultats inférieurs à-b
). Pour garantir des résultats positifs pour toutes les entrées que vous pouvez utiliserr + abs(b)
ou pour correspondre aub
signe s, vous pouvez remplacer la condition parr*b < 0
.r + abs(b)
est UB quandb == INT_MIN
.Je ne pense pas qu'il soit nécessaire de vérifier si le nombre est négatif.
Une fonction simple pour trouver le modulo positif serait la suivante -
Edit: En supposant
N > 0
etN + N - 1 <= INT_MAX
int modulo(int x,int N){ return (x % N + N) %N; }
Cela fonctionnera pour les valeurs positives et négatives de x.
PS d'origine: également comme indiqué par @chux, si vos x et N peuvent atteindre quelque chose comme INT_MAX-1 et INT_MAX respectivement, remplacez-les simplement
int
parlong long int
.Et s'ils franchissent également les limites du long long (c'est-à-dire près de LLONG_MAX), alors vous devez gérer les cas positifs et négatifs séparément comme décrit dans d'autres réponses ici.
la source
N < 0
, le résultat peut être négatif comme dansmodulo(7, -3) --> -2
.x % N + N
Peut également déborder deint
maths, ce qui est un comportement non défini. par exemple, celamodulo(INT_MAX - 1,INT_MAX)
peut entraîner -3.long long int
ou traiter le cas négatif séparément (au prix de perdre la simplicité).Les autres réponses ont expliqué dans C99 ou plus tard, la division d'entiers impliquant des opérandes négatifs tronque toujours vers zéro .
Notez que, dans C89 , si le résultat arrondi à la hausse ou à la baisse est défini par l'implémentation. Parce que
(a/b) * b + a%b
égalea
dans toutes les normes, le résultat de l'%
implication d'opérandes négatifs est également défini par l'implémentation dans C89.la source
%
peut être négatif car il s'agit de l' opérateur de reste , le reste après 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
Le modulo OP recherché est un modulo euclidien classique , non
%
.Pour effectuer un modulo euclidien qui est bien défini chaque fois qu'il
a/b
est défini,a,b
sont 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
la source
Le résultat de l'opération Modulo dépend du signe du numérateur, et donc vous obtenez -2 pour y et z
Voici la référence
http://www.chemie.fu-berlin.de/chemnet/use/info/libc/libc_14.html
la source
En mathématiques, d'où proviennent ces conventions, il n'y a pas d'affirmation selon laquelle l'arithmétique modulo devrait donner un résultat positif.
Par exemple.
1 mod 5 = 1, mais il peut aussi égaler -4. Autrement dit, 1/5 donne un reste 1 de 0 ou -4 de 5. (Les deux facteurs de 5)
De même, -1 mod 5 = -1, mais il peut également être égal à 4. Autrement dit, -1/5 donne un reste -1 à partir de 0 ou 4 à partir de -5. (Les deux facteurs de 5)
Pour en savoir plus, regardez dans les classes d'équivalence en mathématiques.
la source
a
etb
,b <> 0
. Selon le théorème de division euclidienne, il existe exactement une paire d'entiersm
,r
oùa = m * b + r
et0 <= r < abs( b )
. Le ditr
est le résultat de l'opération modulo (mathématique) et par définition est non négatif. Plus de lecture et d'autres liens sur Wikipedia: en.wikipedia.org/wiki/Euclidean_division1 mod 5
est toujours 1.-4 mod 5
peut être 1 aussi, mais ce sont des choses différentes.Selon la norme C99 , section 6.5.5 Opérateurs multiplicatifs , les éléments suivants sont requis:
Conclusion
Le signe du résultat d'une opération de reliquat, selon 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 le 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 négatifs
(-3 / -2) * -2 + -3 % -2 = -3 (-3 / -2) * -2 = -2 (-3 % -2) must be -1
la source
L'opérateur de module donne le reste. L'opérateur de module en c prend généralement le signe du numérateur
De plus, l'opérateur module (reste) ne peut être utilisé qu'avec un type entier et ne peut pas être utilisé avec une virgule flottante.
la source
Je pense qu'il est plus utile de penser
mod
comme il est défini dans l'arithmétique abstraite; non pas comme une opération, mais comme une toute autre classe d'arithmétique, avec différents éléments et différents opérateurs. Cela signifie que l'additionmod 3
n'est pas la même chose que l'addition «normale»; C'est; addition d'entiers.Alors quand vous faites:
5 % -3
Vous essayez de mapper l' entier 5 sur un élément de l'ensemble de
mod -3
. Voici les éléments demod -3
:{ 0, -2, -1 }
Donc:
0 => 0, 1 => -2, 2 => -1, 3 => 0, 4 => -2, 5 => -1
Disons que vous devez rester éveillé pour une raison quelconque 30 heures, combien d'heures vous restera-t-il ce jour-là?
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.la source
Il semble que le problème
/
ne soit pas une opération au sol .int mod(int m, float n) { return m - floor(m/n)*n; }
la source