Où nous allons découvrir comment une spécificité de l’opérateur d’exponentiation, “
**”, peut réserver une mauvaise surprise lorsqu’un type modulo s’invite sournoisement dans l’expression. Les règles de l’algèbre ne semblent plus avoir cours, découvrons comment et pourquoi…
Soit l’application Ada suivante :
Source Ada :
with Ada.Text_IO;
use Ada.Text_IO;
procedure Trap is
type Modular_Type is mod 256;
subtype Index_Type is Natural range 1 .. 3;
M : Modular_Type := 255;
I : Index_Type := 3;
begin
-- 10 ^ 3 = 1000.
-- What will prints the below statement?
if M < 10 ** I then
Put_Line ("`M` is less than `10 ** I`.");
else
Put_Line ("`M` is greater‑than or equal‑to `10 ** I`.");
end if;
end Trap;
Pouvez‑vous, et avant même de vous jetez sur votre compilateur et d’exécuter l’application, prédire son résultat ?
Jouez le jeux, devinez, puis compilez l’application avant de la tester.
La réponse dans le
spoiler ci‑dessous
(cliquez le pour l’afficher)
Spoiler : cliquez sur le cadre pour l'afficher
Le programme affiche « `M` is greater‑than or equal‑to `10 ** I`. »
Pourquoi ?
Remarquez que le programme a put être compilé sans erreur, alors que la partie gauche de la comparaison, est un type Modulo_Type et la partie droite peut sembler être du type Integer.
Et ici est le piège : la partie droite est du type Modulo_Type ! L’expression est en fait implicitement “(10 ** I) mod 256”, qui vaut 232. Et effectivement, 255 est plus grand que 232.
À droite de “10 ** I”, nous avons I, dont le type de base est Integer (sous‑type de Natural, lui‑même sous‑type de Integer). Mais I ne participe pas au type de l’expression, en vertu d’une spécificité de l’opération **. Le type de 10 dans l’expression n’est donc pas non‑plus Integer.
Quel est son type alors ? Il est du type Modulo_Type ! C’est en effet la partie gauche de la comparaison qui transmet son type à la partie droite, qui n’en a initialement pas, type dans lequel I ne joue aucun rôle. Le membre de droite d’une exponentiation, doit en effet toujours être un sous‑type de Natural et donc avoir Integer comme type de base et n’a pas d’incidence sur le type de l’expression, qui ne dépendra que du membre de gauche.
On peut être ici, trompé(e), en pensant à la partie droite seulement de la comparaison, en la croyant du type Integer et en oubliant par mégarde la partie gauche. Contrairement aux habitudes que l’on peut prendre avec Ada, ici, le compilateur ne pourra pas nous aider en détectant l’erreur lui‑même.
Reprenez le programme précédent, en remplaçant “
10 ** I” par “
256 ** I”. Que constatez‑vous ?
Spoiler : cliquez sur le cadre pour l'afficher
Réponse : le programme ne peut plus être compilé, le compilateur objectant que la valeur est hors de l’intervalle de Modular_Type. Ceci confirme de manière informelle, que la partie gauche de l’opération d’exponentiation, est bien du type Modular_Type.
Reprenez le programme initial, en remplaçant “
10 ** I” par “
Natural'Pos (10 ** I)”. Que constatez‑vous ?
Spoiler : cliquez sur le cadre pour l'afficher
Réponse : à l’exécution, vous obtenez une erreur d’intervalle en dehors des limites, en dehors des limites du type de Modular_Type. Ceci est une seconde manière informelle de confirmer que le membre l’expression d’exponentiation est du type Modular_Type.
Remarque en marge : une version spécifique d’un compilateur Ada célèbre, renvoi cette erreur d’intervalle en dehors des limites à la compilation‑même et rejète le programme. Ce comportement n’est pas conforme à la spécification Ada, même si ce n’est pas très grave ici. Si l’expression “10 ** I” était statique, la compilation devrait effectivement échoué. Mais ici, l’expression n’est pas statique au sens stricte du terme, et la compilation devrait se poursuive, même si l’existence d’une erreur à la compilation, pourrait faire l’objet d’un avertissement.