On dispose de nb bits (chapitre 1) et l'on propose une seconde…
Exercice d'entraînement · niveau 3 (difficile) · NSI (première), chapitre 3 — Langages et programmation · Mettre au point : choisir les cas qui séparent
Énoncé
On dispose de nb_bits (chapitre 1) et l'on propose une seconde implémentation, math.floor(math.log2(n)) + 1. Mettre en place un test différentiel sur à . Que se passe-t-il pour ? Qu'en conclure sur cette manière de tester ?
Corrigé
import math
def nb_bits_formule(n):
if n == 0:
return 1
return math.floor(math.log2(n)) + 1
desaccords = [n for n in range(0, 100000) if nb_bits(n) != nb_bits_formule(n)]
print(len(desaccords)) # 0
Aucun désaccord sur valeurs. La conclusion tentante — « les deux fonctions sont correctes » — est fausse.
Le cas .
gros = 2**60
print(nb_bits(gros), nb_bits_formule(gros)) # 61 61
print(nb_bits(gros - 1), nb_bits_formule(gros - 1)) # 60 61
Sur , les deux fonctions divergent : contre . C'est la version par boucle qui a raison — s'écrit avec soixante bits à . La formule échoue parce que math.log2 convertit son argument en flottant : n'est pas représentable exactement en double précision, il est arrondi à , dont le logarithme vaut exactement (chapitre 2).
Les deux leçons.
- Un test différentiel qui passe sur cas ne prouve rien au-delà de ces cas. Le domaine testé, à , tenait tout entier dans la zone où les flottants sont exacts — le défaut était structurellement hors de portée.
- Quand deux implémentations divergent, il faut décider laquelle a raison par le raisonnement, pas par vote. Ici la définition — le nombre de bits de l'écriture binaire — tranche sans ambiguïté en faveur de la boucle.
La règle qui en découle : le domaine de test doit être choisi d'après les frontières du problème (ici : la limite d'exactitude des flottants, ), et non d'après ce qui s'exécute vite.
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.