- Modulo (informatique)
-
Une très grande part des calculs réalisés en informatique sont des calculs d'arithmétique modulaire (d'autres se font, par exemple, en arithmétique saturée). En effet, les données manipulées sont toujours représentées au final comme une suite finie de bits, c’est-à-dire un entier compris entre 0 et n - 1 où n est une puissance entière de 2 et l’arithmétique des entiers doit donc être approchée sur ces ensembles finis. De plus, nombre d'opérations sont réalisées bit à bit, c’est-à-dire dans Z/2Z.
Du fait de cette utilisation au quotidien, des notations spécifiques sont apparues pour cette discipline.
Ainsi, en programmation informatique, on désigne par modulo l’opération de calcul du reste de la division euclidienne.
Si a est un entier quelconque et n un entier strictement positif, on écrira a mod n pour représenter le reste dans {0, ..., n−1} de la division de a par n. Un modulo équivaut donc à la différence entre un dividende et la multiplication de la valeur tronquée du quotient de la division de ce dividende par un quelconque diviseur par ce même diviseur. Ainsi, 9 mod 4 = 9 - (2 * 4) = 1.
On note souvent cette opération
a % n
(notation utilisée dans les langages dérivés de C). Toutefois cette définition est insuffisante car elle ne définit pas le comportement quand le diviseur est négatif, et la notion de reste dans une division euclidienne d'un entier négatif par un entier positif n'est pas claire (en effet le reste pourrait être négatif alors que le diviseur est positif).Cette définition est cependant insuffisante pour la décrire complètement pour le cas où le diviseur n’est pas positif ou les opérandes ne sont pas entiers. Elle cache en fait deux opérations différentes :
Sommaire
Deux définitions de la fonction modulo
Dans la pratique x mod y peut être calculé en utilisant d'autres fonctions. Des différences apparaissent suivant les types des variables utilisées, lesquels contiennent le type entier dans les implémentations courantes. Mais la principale différence réside dans l'interprétation de la partie entière du quotient, en fonction du signe du dividende ou celui du diviseur quand ceux-ci peuvent être négatifs:
- En utilisant la partie entière
floor
,floor(z)
est le plus grand entier inférieur ou égal à z, ce qui retourne un modulo toujours compris entre 0 et le diviseur y (cette interprétation est celle qui facilite le calcul des phénomènes périodiques dans des repères avec une origine arbitraire) :- x mod y =
x - y * floor(x / y)
.
- x mod y =
- En utilisant la fonction de troncature de la partie décimale (désignée par
remain()
dans certains calculateurs et qui retourne toujours un entier positif ; réalisée en C ou en PHP par l'opérateur de base % mais uniquement pour des opérandes entiers ; cette définition parait naturelle mais elle complique le calcul des phénomènes cycliques et oblige à tester l'origine des repères pour éviter d'avoir à tester le signe du dividende x, sachant qu'en général y est connu, constant et positif et correspond à la période du cycle) :x % y = x - y * iPart(x / y)
Interprétation et utilisation des deux définitions
Les deux opérations ci-dessus sont différentes :
- Dans le cas de la fonction partie entière floor, le résultat est négatif pour un modulo avec un entier strictement négatif (en convenant de poser : x mod −y = −((−x) mod y) ; par exemple : 1 mod −3 = −2). Le modulo retourné est donc du même signe que le diviseur y. Nous obtenons une fonction notée
mod()
sur les calculateurs et implémentée dans certains langages de haut niveau incluant Perl et Visual Basic ; dans les feuilles Excel en version française, la fonction est nomméeReste()
. - Perl, C, C++, Java, PHP, etc. définissent aussi l’opérateur % pour effectuer une opération modulo, en faisant allusion à l’opérateur de division / qui en Perl est une division entière vers 0 (c’est-à-dire dont le quotient entier retourné est celui qui a la plus grande valeur absolue inférieure ou égale à la valeur absolue du quotient réel). Dans ce cas, la fonction iPart est toujours inférieure ou égale en valeur absolue au quotient réel, et le modulo retourné est toujours du même signe que le dividende x.
La première toutefois est la seule qui a un sens en arithmétique modulaire, car elle retourne la même valeur de modulo (caractéristique de la classe de congruence) pour tous les entiers positifs ou négatifs qui se réduisent dans la même classe modulaire de Z/nZ, n étant le diviseur. Ce n’est pas vrai de la deuxième fonction qui retourne deux valeurs modulaires possibles pour les non-multiples de n, c’est-à-dire une valeur modulaire positive x pour les valeurs d'entrées positives, et une valeur modulaire négative −x pour les valeurs d'entrée opposées.
Cela s’avère génant pour les calculs cycliques (par exemple calendaires), et c'est pourquoi les calculs calendaires exploitent plutôt la première fonction avec floor() pour réduire les quotients à l’entier égal ou immédiatement inférieur, quel que soit son signe. Dans ce cas, la valeur modulaire retournée est toujours du signe du diviseur (le diviseur étant toutefois positif dans la plupart des calculs cycliques dont les calculs calendaires).
Malheureusement, que ce soit en C, C++, C#, Java, J#, PHP, ou Perl, et pour la plupart des processeurs, l'opération de division entière représentée par l'opérateur de programmation / et le modulo représenté par l’opérateur de programmation % prend toujours la seconde définition (troncature vers 0 du quotient).
L’opération mathématique de division entière utilise la partie entière vers −infini (la fonction floor des bibliothèques mathématiques), et la classe de congruence est calculée avec cette opération qui préserve les cycles de congruence sans discontinuité en 0. L’opération mathématique a en effet l’intéressante propriété suivante :
- mod(x, n) = mod(x + k.n, n), quel que soit l’entier k (positif ou négatif).
Cette propriété traduit la « congruence modulo n » des valeurs x et (x+ k·n). Cette propriété n’est pas respectée par l’opération % prédéfinie en C, C++, Java, PHP, Perl, etc...
Comportement avec des opérandes non entiers
Cependant, les deux définitions permettent à x et y d’être des entiers négatifs, ou des nombres rationnels (ou réels en mathématiques, bien que les systèmes informatiques de calcul numérique ne sachent travailler que sur un sous-ensemble des nombres rationnels, du fait de limites de précision).
Cependant en C, C++, Java, PHP et de nombreux langages, l’opérateur % n’opère sur les types numériques qu’en les convertissant d’abord en entiers (avec l’opération de troncature vers 0, souvent en réduisant aussi l’intervalle de définition à un sous-ensemble d’entiers signés avec un nombre de bits limités : en cas de dépassement, la troncature peut produire un résultat inattendu, car les opérandes sont d'abord chacun convertis par une opération modulo sur le nombre de bits maximum admis dans la représentation numérique des entiers ; dans d'autres implémentation, le résultat peut produire une exception arithmétique de débordement de capacité).
Aussi l’opérateur % réalise en fait l’opération suivante :
- x % y = int(x) - (int(x) div int(y))* int(y) = = int(x) - int( int(x) / int(y) )*int(y)
où l’opérateur div est la division entière de deux entiers, vers zéro, et où la fonction
int()
réalise la conversion d’un paramètre numérique en l’entier le plus proche de valeur absolue inférieure ou égale au paramètre ; mais en pratique même cet entier est lui-même tronqué des bits de poids fort en excès pour la représentation des entiers, ce qui équivaut à réaliser un modulo de cet entier vers une puissance de 2. Aucune exception de débordement de capacité n’est généralement levée pour indiquer cette troncature de bits de poids forts non nuls.Pour ces raisons, l’opérateur %, bien que simple en apparence, n’est pas utilisable sans précaution préalable de vérification de son domaine de validité, où l’opérateur % retourne effectivement un modulo, cet opérateur devra souvent être évité pour les calculs modulaires (par exemple lors de l’évaluation numérique des fonctions trigonométriques cycliques usuelles) au profit de la première définition qui conserve la longueur des cycles et permet une évaluation numérique plus uniforme et plus stable sur un domaine de définition bien plus étendu.
Valeur d’un modulo 0
L’expression x mod 0 n’est habituellement pas définie dans la majorité des systèmes numériques, bien que certains la définissent comme étant x, et en convenant aussi que l’opération duale de division entière retourne 0 quand le diviseur est nul (comme si ce diviseur était lui-même infini), de sorte que les deux opérations associées retournent toujours un couple unique (quotient entier, reste) pour tout couple réel fini (dividende, diviseur), de façon totalement réversible.
Dans les langages les plus courants, l’opération % ne génère aucun résultat si le diviseur est nul, et une exception arithmétique de division par zéro est alors générée.
- En utilisant la partie entière
Wikimedia Foundation. 2010.