NSI · Première

Représenter les données : du bit au texte sans confondre valeur et codage

Un bit prend deux valeurs : 0 ou 1. Un octet rassemble 8 bits. Pour interpréter une suite de bits, il faut connaître son type et sa convention : 11111111 représente 255 en non signé, mais −1 en complément à 2 sur 8 bits. Ce cours te guide des conversions de bases jusqu'aux flottants, à la logique booléenne et aux encodages de texte.

Explications et exemples en accès libre. Ateliers, quiz et cartes avec l’abonnement.

Étude en accès libre
Environ 45 min à 1 h 25
Progression
6 étapes guidées
Vérification
10 questions
Rappel actif
12 cartes
Prévoir mon tempsExplications et exemples en accès libre

Une première étude des explications, schémas, exemples résolus et erreurs expliquées. Les activités, productions, quiz et cartes réservés à l’abonnement ne sont pas comptés ici.

Comprendre1564 mots d’explication et 4 schémas
11 à 18 min
Étudier les exemples et les erreurs18 cas, exemples et activités guidés
36 à 66 min

Étude du cours en accès libre, environ45 min à 1 h 25

Voir le calcul et le temps par étape

Le calcul suit automatiquement les explications et les tâches présentes dans le cours. Ses coefficients sont des repères de planification, pas des temps mesurés auprès d’élèves.

  • Lecture active : 1564 mots, à raison de 160 à 220 mots par minute.
  • Schémas : 4, avec 1 à 2 min pour lire chacun.
  • Exemple guidé : 6 × 2 à 4 min.
  • Suivre le code résolu et ses tests : 6 × 3 à 5 min.
  • Comprendre une erreur expliquée : 6 × 1 à 2 min.
  1. Une valeur, plusieurs écritures : convertir sans magie8 à 16 min
  2. Largeur fixe : combien de bits et que devient le dépassement ?7 à 13 min
  3. Entiers relatifs : lire le même motif avec le complément à 27 à 14 min
  4. Flottants : pourquoi 0,1 + 0,2 ne vaut-il pas exactement 0,3 ?6 à 13 min
  5. Booléens : une table de vérité et un ordre d'évaluation8 à 15 min
  6. Texte : caractères, points de code et octets ne se comptent pas pareil7 à 14 min

Les sous-totaux sont arrondis à la minute, puis additionnés. La fourchette totale est élargie aux cinq minutes voisines. Les étapes ci-dessus comprennent leurs explications et exemples ; elles ne s’ajoutent pas une seconde fois au total.

Adapte ce repère à tes acquis et au soin apporté aux exercices. Les pauses, les reprises, la consultation des sources externes et les révisions suivantes s’ajoutent selon tes besoins.

Avec les ateliers, le projet, le quiz et les cartes : environ 2 h 45 à 4 h 45, à répartir sur plusieurs séances.

Objectifs du cours

Ce que tu vas savoir faire

  • Distinguer information, valeur, type, représentation, base, largeur et encodage.
  • Convertir un entier naturel entre bases 2, 10 et 16 par un algorithme expliqué et testé.
  • Calculer une largeur minimale, détecter un dépassement et interpréter une addition non signée sur largeur fixe.
  • Encoder et décoder un entier relatif en complément à 2 sur une largeur donnée.
  • Expliquer l'approximation des flottants et choisir une comparaison par tolérance adaptée.
  • Dresser une table de vérité, raisonner sur l'évaluation séquentielle et relier caractère, point de code et octets.
01

Étape du cours · 8 à 16 min

Une valeur, plusieurs écritures : convertir sans magie

Un bit est un chiffre binaire, 0 ou 1 ; un octet est un groupe de huit bits. En base b, un chiffre a une valeur de 0 à b − 1. En hexadécimal, les lettres A, B, C, D, E et F représentent respectivement 10, 11, 12, 13, 14 et 15. La position la plus à droite a le poids 1, puis viennent b, b², b³… Les zéros à gauche ne changent pas la valeur, mais indiquent parfois une largeur imposée.

Le nombre quarante-deux ne change pas lorsque l'on écrit 42 en base 10, 101010 en base 2 ou 2A en base 16. Dans une écriture positionnelle, chaque chiffre multiplie une puissance de la base. Lire un mot de chiffres revient à répéter l'opération accumulateur = accumulateur × base + chiffre. Écrire un entier revient à extraire les restes de divisions successives, puis à les lire dans l'ordre inverse.

Deux invariants rendent les algorithmes explicables. Pendant la lecture, l'accumulateur vaut le préfixe déjà traité. Pendant l'écriture, la valeur initiale égale toujours valeur restante × base^k plus la valeur des k restes extraits. La fonction contrôle aussi le domaine : base comprise entre 2 et 16, entier naturel et chiffres autorisés. Un outil intégré peut vérifier le résultat, mais ne remplace pas le raisonnement.

Pour passer directement du binaire à l'hexadécimal, regroupe les bits par quatre à partir de la droite : quatre bits représentent 16 valeurs, de 0 à 15. Complète seulement le groupe de gauche par des zéros si nécessaire. Ainsi 1011010 devient 0101 1010, puis 5A. En Python, bin(90), hex(90) et int('5A', 16) contrôlent ces résultats ; les préfixes 0b et 0x indiquent la base, ils ne font pas partie des chiffres.

Dans les laboratoires, assert rend un contrat ou un résultat attendu vérifiable pendant l'étude. Lance les exemples avec Python 3 sans option -O : cette option désactive les assertions. Une application recevant des données externes doit effectuer une validation explicite, indépendamment de ces assertions de travail.

Voir pour comprendre

42 : suivre les restes, puis changer de sens

Une ligne correspond à une division euclidienne par 2.

42 : suivre les restes, puis changer de sens
EntierQuotientReste
42210
21101
1050
521
210
101

Lis le schéma. Descends pour suivre les quotients. Remonte ensuite la colonne des restes : 1, 0, 1, 0, 1, 0.

Les derniers restes extraits ont les poids les plus forts. 101010 en base 2 vaut 32 + 8 + 2, soit 42.

Exemples résolus et erreurs expliquées

Convertir 90 et contrôler dans les deux sens

  1. En partant des puissances de 2, 90 = 64 + 16 + 8 + 2. Les poids 64, 32, 16, 8, 4, 2, 1 portent donc les bits 1, 0, 1, 1, 0, 1, 0.
  2. L'écriture binaire est 1011010. Regroupe depuis la droite : 0101 1010 ; 0101 vaut 5 et 1010 vaut 10, représenté par A.
  3. Vérifie sans reprendre le même algorithme : 5 × 16 + 10 = 90. Les écritures 90 en décimal, 1011010 en binaire et 5A en hexadécimal désignent la même valeur.

Conclusion. Le contrôle retour vérifie la valeur, tandis que le choix d'une largeur détermine les zéros de tête.

Laboratoire de code

Languepython

Butimplémenter dans les deux sens une conversion positionnelle sans utiliser int(texte, base)

Code solution
DIGITS = "0123456789ABCDEF"

def vers_base(n, base):
    assert type(n) is int and n >= 0
    assert type(base) is int and 2 <= base <= len(DIGITS)
    if n == 0:
        return "0"
    resultat = []
    while n > 0:
        n, reste = divmod(n, base)
        resultat.append(DIGITS[reste])
    return "".join(reversed(resultat))

def depuis_base(texte, base):
    assert isinstance(texte, str) and texte and type(base) is int and 2 <= base <= len(DIGITS)
    valeur = 0
    for caractere in texte.upper():
        chiffre = DIGITS.find(caractere)
        if chiffre < 0 or chiffre >= base:
            raise ValueError("chiffre incompatible avec la base")
        valeur = valeur * base + chiffre
    return valeur

Tests

assert vers_base(0, 2) == "0"
assert vers_base(42, 2) == "101010"
assert vers_base(255, 16) == "FF"
assert depuis_base("101010", 2) == 42
assert depuis_base("2A", 16) == 42
assert all(depuis_base(vers_base(n, 16), 16) == n for n in range(300))
try:
    vers_base(True, 2)
except AssertionError:
    pass
else:
    raise AssertionError('entrée invalide acceptée')
try:
    vers_base(6, 2.5)
except AssertionError:
    pass
else:
    raise AssertionError('entrée invalide acceptée')
try:
    depuis_base('2', 2)
except ValueError:
    pass
else:
    raise AssertionError('entrée invalide acceptée')

Trace

  • n=42 : divmod(42, 2) donne quotient 21, reste 0, chiffres inversés [0]
  • n=21 : quotient 10, reste 1, chiffres inversés [0, 1]
  • les divisions continuent jusqu'à 0, puis l'inversion donne 101010 et depuis_base reconstruit 42

Clinique de bogue

Indice observébinaire(6) renvoie 011 au lieu de 110 et binaire(0) renvoie une chaîne vide

CauseLes restes arrivent du poids faible au poids fort : les concaténer à droite conserve l'ordre inverse. Le cas zéro n'entre jamais dans la boucle et doit être traité explicitement.

02

Étape du cours · 7 à 13 min

Largeur fixe : combien de bits et que devient le dépassement ?

Avec w bits, un entier naturel peut prendre 2^w motifs, de 0 à 2^w - 1. Le nombre minimal de bits pour n strictement positif est l'unique w tel que 2^(w-1) ≤ n < 2^w ; zéro exige par convention un bit si l'on veut l'écrire. Une somme peut demander un bit de plus : sur 8 bits, 250 + 10 vaut mathématiquement 260 mais le motif stocké représente 4 si l'on ne conserve que les huit bits faibles.

Python agrandit automatiquement ses entiers, ce qui est utile mais cache le comportement d'une largeur matérielle. Le laboratoire renvoie donc séparément la valeur stockée et un booléen de dépassement. Cette séparation évite de présenter 4 comme résultat mathématique de 250 + 10 : 4 est le résultat d'une opération définie modulo 256, et l'overflow est une information nécessaire à l'interprétation.

Pour une somme de deux entiers naturels sur au plus w bits, w + 1 bits suffisent. Pour un produit d'entiers écrits sur p et q bits, p + q bits suffisent, car le produit est strictement inférieur à 2^(p+q). Ce sont des garanties, pas toujours des minimums : calcule la valeur puis compare-la aux puissances de 2 si tu cherches la largeur minimale. Les tailles 8, 16, 32 et 64 bits sont courantes ; leur domaine dépend aussi de la convention signée ou non signée.

Exemples résolus et erreurs expliquées

Additionner en binaire et dimensionner un résultat

  1. Calcule 1011 + 0110, soit 11 + 6. De droite à gauche : 1 + 0 donne 1 ; 1 + 1 donne 0 et une retenue de 1.
  2. La colonne suivante reçoit 0 + 1 + 1 : écris 0 et reporte 1. Enfin 1 + 0 + 1 donne encore 0 avec une retenue finale de 1. Le résultat est 10001, soit 17.
  3. Sur quatre bits non signés, seuls 0001 sont conservés et le dépassement est signalé. Le résultat mathématique reste 17, pas 1.
  4. Pour 19 × 27 = 513, chaque facteur tient sur cinq bits. Leur produit exige dix bits car 2⁹ = 512 ≤ 513 < 1024 = 2¹⁰. En revanche 19 + 27 = 46 exige six bits.

Conclusion. Distingue la valeur calculée, les bits conservés et l'information de dépassement.

Laboratoire de code

Languepython

Butcalculer une largeur minimale et simuler une addition non signée sur largeur fixe

Code solution
def bits_necessaires(n):
    assert type(n) is int and n >= 0
    largeur = 1
    while n >= 2 ** largeur:
        largeur += 1
    return largeur

def addition_non_signee(a, b, largeur):
    assert type(largeur) is int and largeur > 0
    limite = 2 ** largeur
    if not (type(a) is int and type(b) is int and 0 <= a < limite and 0 <= b < limite):
        raise ValueError("opérande hors de la largeur annoncée")
    somme = a + b
    return somme % limite, somme >= limite

Tests

assert bits_necessaires(0) == 1
assert bits_necessaires(1) == 1
assert bits_necessaires(2) == 2
assert bits_necessaires(255) == 8
assert bits_necessaires(256) == 9
assert addition_non_signee(100, 27, 8) == (127, False)
assert addition_non_signee(250, 10, 8) == (4, True)
assert addition_non_signee(15, 1, 4) == (0, True)
try:
    addition_non_signee(True, 1, 4)
except ValueError:
    pass
else:
    raise AssertionError('entrée invalide acceptée')

Trace

  • pour n=255, 255 ≥ 2^1 puis ... puis 2^7 : la largeur augmente
  • 255 < 2^8 : la boucle s'arrête à 8 bits
  • pour 250+10 sur 8 bits, limite=256, somme=260, valeur stockée=4 et overflow=True

Clinique de bogue

Indice observétient_sur(256, 8) renvoie True alors que 256 nécessite 9 bits

CauseLa borne supérieure des valeurs représentables sur w bits est 2^w - 1. Employer ≤ 2^w inclut un motif supplémentaire qui n'existe pas et crée une erreur de frontière.

03

Étape du cours · 7 à 14 min

Entiers relatifs : lire le même motif avec le complément à 2

Sur w bits en complément à 2, les valeurs vont de -2^(w-1) à 2^(w-1)-1. Un entier positif ou nul garde son écriture habituelle. Un entier négatif n est encodé par n modulo 2^w ; ainsi -1 devient le motif maximal. Pour décoder, un motif inférieur à 2^(w-1) reste positif ou nul, tandis qu'un motif supérieur ou égal à ce seuil reçoit la valeur motif - 2^w.

Le bit de poids fort ne doit pas être retiré puis lu isolément comme un signe. Il contribue avec le poids -2^(w-1), ce qui explique l'asymétrie de l'intervalle et l'unicité de zéro. Le contrat vérifie la largeur et refuse toute valeur hors domaine au lieu de tronquer silencieusement. Les tests couvrent zéro, les deux extrémités, -1 et l'aller-retour de tout l'intervalle sur 8 bits.

Voir pour comprendre

Huit bits, deux interprétations

Même motif de huit bits, lu comme non signé ou comme entier signé en complément à 2 (C2).

Huit bits, deux interprétations
MotifNon signéSigné (C2)
01111111127127
10000000128−128
11111111255−1

Lis le schéma. Compare la deuxième et la troisième ligne. Dès que le bit fort vaut 1, retranche 256 à la valeur non signée.

Sur 8 bits signés, le seuil 128 appartient déjà aux valeurs négatives. Aucun motif supplémentaire n'apparaît.

Exemples résolus et erreurs expliquées

Encoder −37 sur huit bits, puis retrouver −37

  1. Vérifie le domaine : sur huit bits signés, −128 ≤ −37 ≤ 127. Le codage est possible.
  2. Écris +37 sur huit bits : 00100101. Inverse tous les bits : 11011010. Ajoute 1 : 11011011.
  3. Contrôle par la valeur non signée : 11011011 vaut 219 ; comme son premier bit est 1, 219 − 256 = −37.
  4. Contrôle autrement avec le poids négatif du bit fort : −128 + 64 + 16 + 8 + 2 + 1 = −37.

Conclusion. La méthode inversion puis ajout de 1 retrouve le codage modulo 256. Elle garde toujours la largeur annoncée.

Laboratoire de code

Languepython

Butencoder et décoder exactement le complément à 2 sur une largeur contrôlée

Code solution
def encoder_relatif(n, largeur):
    assert type(largeur) is int and largeur > 0
    minimum = -(2 ** (largeur - 1))
    maximum = 2 ** (largeur - 1) - 1
    if type(n) is not int or not minimum <= n <= maximum:
        raise ValueError("entier hors domaine")
    return n % (2 ** largeur)

def decoder_relatif(motif, largeur):
    assert type(largeur) is int and largeur > 0
    limite = 2 ** largeur
    if type(motif) is not int or not 0 <= motif < limite:
        raise ValueError("motif hors largeur")
    seuil = 2 ** (largeur - 1)
    return motif if motif < seuil else motif - limite

Tests

assert encoder_relatif(-1, 8) == 255
assert encoder_relatif(-128, 8) == 128
assert encoder_relatif(127, 8) == 127
assert decoder_relatif(255, 8) == -1
assert decoder_relatif(128, 8) == -128
assert all(decoder_relatif(encoder_relatif(n, 8), 8) == n for n in range(-128, 128))
assert decoder_relatif(8, 4) == -8
try:
    encoder_relatif(True, 8)
except ValueError:
    pass
else:
    raise AssertionError('entrée invalide acceptée')
try:
    decoder_relatif(1.5, 8)
except ValueError:
    pass
else:
    raise AssertionError('entrée invalide acceptée')

Trace

  • sur 8 bits, limite=256 et seuil=128
  • encoder -37 : -37 modulo 256 donne le motif 219, soit 11011011
  • décoder 219 : 219 ≥ 128, donc 219-256=-37 ; l'aller-retour conserve la valeur

Clinique de bogue

Indice observédecoder(128, 8) renvoie 128, valeur impossible sur 8 bits signés, au lieu de -128

CauseLe motif égal au seuil possède déjà le bit de poids fort à 1. La branche négative commence donc à motif ≥ seuil, pas strictement au-dessus ; c'est un cas limite essentiel.

04

Étape du cours · 6 à 13 min

Flottants : pourquoi 0,1 + 0,2 ne vaut-il pas exactement 0,3 ?

Un flottant stocke un nombre fini de chiffres binaires avec une échelle. Certaines fractions, comme 1/2 ou 1/4, ont une écriture binaire finie ; 1/10 se répète indéfiniment et doit être arrondi. L'affichage décimal masque souvent l'approximation, mais les opérations la propagent. La question à contrôler est donc la précision nécessaire au résultat, et non le seul nombre de chiffres affichés.

Une comparaison robuste fixe une tolérance cohérente avec la grandeur mesurée. Une tolérance absolue protège le voisinage de zéro ; une tolérance relative s'adapte à l'échelle. Elle ne sert pas à déclarer égaux n'importe quels résultats : le domaine doit justifier l'erreur acceptable. Pour de l'argent, compter une unité entière comme le centime ou employer un type décimal peut être préférable à une tolérance improvisée.

Après la virgule binaire, les poids sont 1/2, 1/4, 1/8, 1/16… Pour convertir une fraction entre 0 et 1, multiplie le reste par 2, écris sa partie entière (0 ou 1), puis recommence avec sa partie fractionnaire. Un reste nul termine l'écriture ; un reste déjà rencontré annonce une répétition. Il faut donc distinguer une écriture mathématique infinie et son stockage fini.

Exemples résolus et erreurs expliquées

Calculer l'écriture binaire d'une fraction

  1. Pour 0,25 : 0,25 × 2 = 0,5, donc le premier bit vaut 0. Puis 0,5 × 2 = 1 : le deuxième bit vaut 1 et le reste est nul. Ainsi 0,25 = 0,01 en base 2.
  2. Pour 1/3 : le double vaut 2/3, premier bit 0. Le double du nouveau reste vaut 4/3 = 1 + 1/3, deuxième bit 1. Le reste initial revient : 0,010101… ne se termine pas.
  3. Pour 0,1, les restes exacts après extraction sont 0,2 ; 0,4 ; 0,8 ; 0,6 ; 0,2. Le retour à 0,2 donne 0,000110011… en binaire.
  4. Le stockage coupe cette écriture à une précision finie et arrondit. Dans le Python usuel à flottants binaires, 0.1 + 0.2 donne 0.30000000000000004. Une tolérance absolue de 10⁻¹² accepte cet écart, mais pas un écart de 0,02.

Conclusion. Une fraction dont le dénominateur réduit est une puissance de 2 possède une écriture binaire finie. Une autre base peut changer ce caractère fini.

Laboratoire de code

Languepython

Butobserver l'approximation puis comparer des mesures avec tolérances explicites

Code solution
from math import isfinite

def presque_egaux(a, b, tolerance_absolue=1e-12, tolerance_relative=1e-9):
    # Contrat : valeurs finies, tolérances finies et non négatives.
    assert all(isfinite(v) for v in (a, b, tolerance_absolue, tolerance_relative))
    assert tolerance_absolue >= 0 and tolerance_relative >= 0
    ecart = abs(a - b)
    echelle = max(abs(a), abs(b))
    return ecart <= max(tolerance_absolue, tolerance_relative * echelle)

def somme_dixiemes(nombre):
    assert type(nombre) is int and nombre >= 0
    total = 0.0
    for _ in range(nombre):
        total += 0.1
    return total

def euros_en_centimes(euros_entiers, centimes):
    assert type(euros_entiers) is int and type(centimes) is int and euros_entiers >= 0 and 0 <= centimes < 100
    return 100 * euros_entiers + centimes

Tests

assert 0.1 + 0.2 != 0.3
assert presque_egaux(0.1 + 0.2, 0.3)
assert presque_egaux(somme_dixiemes(10), 1.0)
assert not presque_egaux(1.0, 1.01)
assert presque_egaux(1_000_000.0, 1_000_000.0005, tolerance_relative=1e-9)
assert euros_en_centimes(12, 35) == 1235
assert not presque_egaux(1.0, 1.02, tolerance_absolue=0.01, tolerance_relative=0)
try:
    presque_egaux(1.0, 1.0, tolerance_absolue=-1)
except AssertionError:
    pass
else:
    raise AssertionError('entrée invalide acceptée')

Trace

  • 0,1 est arrondi au flottant binaire représentable le plus proche
  • l'addition de deux approximations produit 0.30000000000000004 dans cet exemple Python
  • l'écart est environ 5,55×10^-17, inférieur à la tolérance choisie, donc presque_egaux renvoie True

Clinique de bogue

Indice observétrois_dixiemes() renvoie False alors que l'intention est de vérifier une égalité de mesures décimales

CauseLe code compare les motifs flottants exactement. Les arrondis de chaque opération ne produisent pas nécessairement le même flottant que le littéral 0.3 ; il faut exprimer la précision du problème.

05

Étape du cours · 8 à 15 min

Booléens : une table de vérité et un ordre d'évaluation

Un booléen vaut vrai (True) ou faux (False). not a inverse la valeur de a. a and b est vrai seulement si les deux propositions sont vraies ; a or b est vrai dès qu'au moins l'une est vraie, y compris lorsque les deux le sont. Avec deux variables, énumère (faux, faux), (faux, vrai), (vrai, faux), (vrai, vrai) : aucune ligne ne doit manquer.

Une expression booléenne associe des propositions par and, or et not. Sa table de vérité énumère toutes les valuations possibles et permet de vérifier équivalence ou contradiction. Par exemple, not (a and b) et (not a) or (not b) ont la même colonne de résultats. Cette preuve exhaustive fonctionne parce que deux variables ne donnent que quatre lignes.

Dans un programme, and et or sont évalués de gauche à droite et peuvent s'arrêter tôt. Dans liste != [] and liste[0] > 0, la seconde partie n'est pas calculée lorsque la liste est vide. Inverser les termes provoque une erreur. La logique du résultat et la sécurité de l'évaluation sont donc deux propriétés à examiner séparément, notamment pour des gardes d'accès ou des données facultatives.

Le ou exclusif, ou XOR, est vrai lorsque les deux booléens sont différents. Pour de vrais booléens Python, a != b exprime cette opération. Il relie logique et calcul : pour additionner deux bits sans retenue entrante, XOR donne le bit de somme et AND donne la retenue. Attention : ^ n'est pas une puissance en Python ; sur les entiers, il agit bit par bit. Les opérateurs and et or peuvent renvoyer un opérande non booléen : les tables ci-dessous portent bien sur des propositions booléennes.

Voir pour comprendre

Deux propositions, quatre cas à vérifier

Les entrées a et b sont des booléens. XOR donne le bit de somme et AND la retenue d'une addition de deux bits sans retenue entrante.

Deux propositions, quatre cas à vérifier
(a, b)ANDORXOR
(0, 0)000
(0, 1)011
(1, 0)011
(1, 1)110

Lis le schéma. Lis chaque couple de la première colonne. Sur la dernière ligne, compare OR et XOR : vrai ou vrai est vrai, mais vrai XOR vrai est faux.

0 signifie faux et 1 signifie vrai dans cette table. Le ou inclusif et le ou exclusif ne se confondent pas.

Exemples résolus et erreurs expliquées

Construire une table composée et un demi-additionneur

  1. Pour not (a and b), calcule d'abord a and b, puis inverse son résultat. Sur les quatre couples dans l'ordre du tableau, tu obtiens vrai, vrai, vrai, faux.
  2. Calcule séparément (not a) or (not b) : tu retrouves vrai, vrai, vrai, faux. L'égalité des quatre lignes démontre cette équivalence pour deux booléens.
  3. Additionne maintenant deux bits égaux à 1, sans retenue entrante. XOR vaut 0 et AND vaut 1 : la somme binaire est 10, soit 2. OR vaudrait 1 et ne donnerait donc pas le bon bit de somme.

Conclusion. Une table vérifie toute la logique finie ; une trace vérifie en plus l'ordre réel d'exécution du programme.

Laboratoire de code

Languepython

Butconstruire une table de vérité et utiliser une garde à court-circuit

Code solution
def table_de_morgan():
    lignes = []
    for a in (False, True):
        for b in (False, True):
            gauche = not (a and b)
            droite = (not a) or (not b)
            lignes.append((a, b, gauche, droite))
    return lignes

def premier_positif(valeurs):
    return bool(valeurs) and valeurs[0] > 0

def peut_publier(est_auteur, est_relu, est_bloque):
    return est_auteur and est_relu and not est_bloque

def demi_additionneur(a, b):
    assert type(a) is bool and type(b) is bool
    return a != b, a and b

Tests

assert all(gauche == droite for _, _, gauche, droite in table_de_morgan())
assert len(table_de_morgan()) == 4
assert premier_positif([3, -1])
assert not premier_positif([])
assert not premier_positif([-3])
assert peut_publier(True, True, False)
assert not peut_publier(True, False, False)
assert not peut_publier(True, True, True)
assert [demi_additionneur(a, b) for a in (False, True) for b in (False, True)] == [(False, False), (True, False), (True, False), (False, True)]

Trace

  • a=False, b=False : a and b vaut False, donc not(...) vaut True
  • pour la même ligne, not a vaut True ; True or True s'arrête et vaut True
  • sur une liste vide, bool(valeurs) vaut False ; la lecture valeurs[0] n'est jamais exécutée

Clinique de bogue

Indice observépremier_positif([]) lève IndexError au lieu de renvoyer False

CauseLa lecture de l'index 0 est placée avant le test d'existence. L'évaluation séquentielle ne protège que les expressions situées après une condition qui a déjà échoué.

06

Étape du cours · 7 à 14 min

Texte : caractères, points de code et octets ne se comptent pas pareil

Unicode attribue un point de code à des caractères abstraits ; un encodage transforme une suite de points de code en octets. ASCII ne représente qu'un répertoire limité et ses codes 0 à 127 sont compatibles avec UTF-8. ISO-8859-1 encode certains caractères européens sur un octet, mais ne couvre ni toutes les écritures ni les emoji. UTF-8 emploie un à quatre octets par point de code et préserve les octets ASCII.

La chaîne Python é a une longueur d'un point de code et occupe deux octets en UTF-8. Une apparence identique peut aussi être composée de e puis d'un accent combinant, soit deux points de code. Un programme doit donc annoncer ce qu'il mesure. Le cycle encoder puis décoder avec le même encodage doit restituer la chaîne ; changer d'encodage ou ignorer une erreur peut altérer les données et parfois leur sens.

Voir pour comprendre

Un même aspect, deux suites de points de code

On compare ici é précomposé et e suivi d'un accent combinant, pas toutes les manières d'afficher un caractère.

Un même aspect, deux suites de points de code
FormePoints de codeUTF-8 (hex.)
éU+00E9 : 1 pointC3 A9 : 2 octets
e + accent aiguU+0065, U+0301 : 2 points65 CC 81 : 3 octets

Lis le schéma. Lis une ligne de gauche à droite, puis compare les deux comptes : même aspect ne signifie pas même suite numérique.

len compte les points de code de la chaîne Python. La taille transportée se calcule après l'encodage ; l'apparence ne suffit pas.

Exemples résolus et erreurs expliquées

Convertir un petit texte de Latin-1 vers UTF-8

  1. Un fichier contient l'octet E9 en hexadécimal, soit 233 en décimal. Son encodage annoncé est ISO-8859-1 (Latin-1) : décoder cet octet donne le caractère é.
  2. Encode ce caractère en UTF-8 : les octets obtenus sont C3 A9, soit 195 et 169. La conversion passe par le texte décodé, pas par un changement de nom du fichier.
  3. En Python, bytes([233]).decode('latin-1').encode('utf-8') produit les mêmes octets que 'é'.encode('utf-8'). Reviens au texte avec .decode('utf-8') pour vérifier.
  4. Un passage vers ASCII échoue pour é, car ce répertoire ne le contient pas. Ne supprime pas silencieusement le caractère : choisis un encodage adapté ou une transformation explicitement demandée.

Conclusion. Dans cet exemple, les octets du fichier sont représentés en mémoire par une constante. Aucun fichier personnel n'est lu ni modifié.

Laboratoire de code

Languepython

Butobserver points de code, octets UTF-8 et aller-retour d'un texte multilingue

Code solution
def rapport_texte(texte):
    octets = texte.encode("utf-8")
    return {
        "points_de_code": [ord(caractere) for caractere in texte],
        "nombre_points_de_code": len(texte),
        "octets_utf8": list(octets),
        "nombre_octets_utf8": len(octets),
        "aller_retour": octets.decode("utf-8")
    }

def est_ascii(texte):
    return all(ord(caractere) < 128 for caractere in texte)

Tests

assert rapport_texte("A")["octets_utf8"] == [65]
assert rapport_texte("é")["nombre_points_de_code"] == 1
assert rapport_texte("é")["nombre_octets_utf8"] == 2
assert rapport_texte("漢")["nombre_octets_utf8"] == 3
assert rapport_texte("Aé漢")["aller_retour"] == "Aé漢"
assert est_ascii("NSI 2026")
assert not est_ascii("élève")
assert rapport_texte('e\u0301')['nombre_points_de_code'] == 2
assert rapport_texte('e\u0301')['octets_utf8'] == [101, 204, 129]
assert rapport_texte('Aé漢')['nombre_octets_utf8'] == 6
assert bytes([233]).decode('latin-1').encode('utf-8') == bytes([195, 169])
assert bytes([195, 169]).decode('utf-8') == 'é'

Trace

  • ord('é') vaut 233 : un point de code dans la chaîne Python
  • UTF-8 encode ce point par les deux octets 195 et 169
  • décoder exactement ces octets en UTF-8 restitue é ; les compter comme deux caractères serait une confusion de niveau

Clinique de bogue

Indice observétaille_message('é') annonce 1 octet alors que l'envoi UTF-8 en utilise 2

Causelen sur une chaîne Python compte ici les points de code, pas les octets de l'encodage. La taille transportée n'existe qu'après avoir choisi et appliqué un encodage.

Poursuivre avec l’abonnement

Passe de l’exemple à ton propre raisonnement

Les explications, schémas et exemples résolus restent en accès libre.

Avec l’abonnement, essaie les ateliers, réponds au quiz et reprends les idées signalées par tes erreurs. Les cartes du chapitre permettent ensuite de t’entraîner au rappel.

10 questions · 12 cartes. Ton bilan et tes révisions restent dans ce navigateur.

Comparer les formules

Vérifier et prolonger

Sources du cours

Édition Maxdecours · Vérifié le .

Spécialité NSI, Première générale · programme du BO spécial du 22 janvier 2019

  1. Programme officiel NSI Premièrecontenus, capacités attendues, limites et démarche de projet · consulté le 2026-09-05
  2. Éduscol, programmes et ressources NSIétat en vigueur et ressources d'accompagnement · consulté le 2026-09-05
  3. Éduscol, représentation des entiers naturelsprogression et activités institutionnelles sur bases et largeur · consulté le 2026-09-05
  4. Éduscol, représentation des entiers relatifscomplément à 2 et erreurs de frontière · consulté le 2026-09-05
  5. Python, tutoriel sur les nombres flottantscomportement observé de Python et comparaison approchée · consulté le 2026-09-05
  6. Unicode Consortium, présentation du standard Unicodedistinction entre répertoire de caractères, points de code et encodages · consulté le 2026-09-05
  7. Python, types standards : booléens, entiers et chaînescontrats de types, opérations booléennes et distinction str/bytes · consulté le 2026-09-05
  8. Python, guide Unicodeencodage, décodage et conversion des octets d'un texte · consulté le 2026-09-05