Le jeu de tests d'une division euclidienne
Exercice · niveau 2 · informatique (MP2I/MPI), chapitre 4 — Discipline de programmation, validation et test
Énoncé
Écrire un jeu de tests pour division(a, b, &q, &r) de l'exercice 1.8, qui range le quotient et le reste de par . Partitionner le domaine, éprouver les limites. Que se passe-t-il si l'on retire la précondition et ?
Corrigé
Le partitionnement. La postcondition est . Les classes qui comptent :
| Classe | Représentant | Ce qu'elle éprouve |
|---|---|---|
| division exacte | ||
| division inexacte | le cas nominal | |
| dividende nul | ||
| , | ||
| , | ||
| diviseur | limite basse du diviseur | |
| grandes valeurs | pas de débordement |
void tester_division(void) {
int q, r;
division(6, 3, &q, &r); assert(q == 2 && r == 0);
division(7, 2, &q, &r); assert(q == 3 && r == 1);
division(0, 5, &q, &r); assert(q == 0 && r == 0);
division(2, 7, &q, &r); assert(q == 0 && r == 2);
division(5, 5, &q, &r); assert(q == 1 && r == 0);
division(7, 1, &q, &r); assert(q == 7 && r == 0);
division(INT_MAX, 2, &q, &r);
assert(2 * (long) q + r == (long) INT_MAX); /* la POSTCONDITION elle-meme */
printf("division : tous les tests passent\n");
}
Le dernier test ne compare pas à une constante écrite à la main : il vérifie la postcondition. C'est la bonne forme dès que la valeur attendue est pénible à calculer, et elle attrape les fautes qu'un jeu de constantes recopiées laisserait passer.
Sans la précondition. Les signes changent tout, et les langages ne s'accordent pas. Mesuré :
| C | OCaml | Python | |||||
|---|---|---|---|---|---|---|---|
| `a/b` | `a%b` | `a/b` | `a mod b` | `a//b` | `a%b` | ||
C et OCaml tronquent vers zéro : le reste porte le signe du dividende. Python arrondit vers le bas : le reste porte le signe du diviseur. L'identité est vérifiée dans les trois — c'est la seule chose qu'ils garantissent tous.
Écrire « précondition : et » n'est pas une paresse : c'est un refus explicite de trancher une question dont la réponse n'est pas universelle. Si l'on veut un reste toujours positif — la convention mathématique du reste euclidien —, il faut l'écrire, et le tester :
/* Range dans *q, *r le quotient et le reste EUCLIDIENS : 0 <= *r < |b|.
Precondition : b != 0. */
void division_euclidienne(int a, int b, int* q, int* r) {
assert(b != 0 && q != NULL && r != NULL);
*q = a / b; *r = a % b;
if (*r < 0) { if (b > 0) { *q = *q - 1; *r = *r + b; }
else { *q = *q + 1; *r = *r - b; } }
}
Et le jeu de tests doit alors couvrir les quatre combinaisons de signes, plus les cas pour chacune : c'est le partitionnement qui l'impose, pas l'intuition. L'annexe OCaml prend la même précaution en n'exigeant mod sans rappel que « quand toutes les grandeurs sont positives » (chapitre chap:langage-ocaml).
Les autres exercices de ce chapitre Le cours du chapitre
Un blocage sur cet exercice ? Le tuteur d'Adloun guide par questions, sans donner la réponse.