La conversion binaire en Python semble triviale au premier abord, mais elle soulève vite des questions plus utiles que la simple transformation d’un entier en chaîne. Dans cet article, je vais expliquer comment utiliser bin(), quand préférer format() ou les f-strings, comment gérer les nombres négatifs, et pourquoi la représentation binaire n’est pas la même chose qu’un vrai encodage de données. Le but est simple: vous donner des réflexes propres, réutilisables, et assez solides pour un script, un outil réseau ou un projet de sécurité.
Ce qu’il faut retenir avant de manipuler le binaire en Python
-
bin()renvoie une chaîne avec le préfixe0b, pas un nombre. - Pour un affichage plus propre,
format(n, 'b')etf'{n:b}'sont souvent plus pratiques. - Les nombres négatifs ne sont pas affichés en complément à deux par défaut.
-
bit_length()aide à calculer la largeur binaire utile, etbit_count()compte les bits à 1. - Pour transporter des données,
to_bytes()est plus pertinent quebin().
Comprendre ce que renvoie bin()
bin() convertit un entier en chaîne binaire préfixée par 0b. C’est pratique pour le débogage, les démonstrations et l’inspection rapide d’une valeur, parce que le résultat reste lisible immédiatement et reste, en plus, une expression Python valide. Autrement dit, je peux afficher une valeur en base 2 sans perdre le signe ni me demander comment la reconstruire ensuite.
bin(3) # '0b11'
bin(14) # '0b1110'
bin(-10) # '-0b1010'Le point qui piège le plus souvent, c’est le format des nombres négatifs. bin() n’affiche pas un complément à deux sur une largeur donnée; il affiche simplement le signe moins suivi de la valeur absolue en binaire. Si vous travaillez sur des masques réseau, des registres ou des octets, cette nuance change tout. Une fois ce format compris, le vrai sujet devient la manière la plus propre de l’afficher ou de l’intégrer dans du code.
Convertir un entier proprement

Pour afficher un nombre en binaire, je n’utilise pas toujours bin(). En pratique, format() et les f-strings donnent souvent un code plus lisible, surtout quand on veut contrôler le préfixe ou la largeur. La différence est petite à l’écran, mais elle devient nette dès qu’on écrit du code destiné à être relu ou maintenu.
n = 14
bin(n) # '0b1110'
format(n, 'b') # '1110'
format(n, '#b') # '0b1110'
f'{n:b}' # '1110'
f'{n:#b}' # '0b1110'Le format 'b' affiche la valeur en base 2 sans préfixe, tandis que '#b' active la forme dite alternative, avec 0b. Pour un affichage technique, j’aime bien f'{n:#b}' quand je veux garder le repère visuel, et f'{n:b}' quand je veux une sortie plus compacte. C’est souvent plus élégant que de trancher la chaîne à la main avec un [2:], surtout quand le signe entre en jeu.
n = -10
format(n, 'b') # '-1010'
format(n, '#b') # '-0b1010'Si vous devez afficher un nombre sur une largeur fixe, pensez aussi au zéro à gauche. Sur un entier positif, c’est simple et très utile pour lire des octets ou des masques de bits:
n = 13
width = max(1, n.bit_length())
format(n, f'0{width}b') # '1101'Quand le besoin dépasse l’affichage simple, il faut regarder les cas limites de plus près, parce que c’est là que les erreurs deviennent visibles.
Les cas qui piègent le plus souvent
Le premier piège, c’est de croire que bin() sait gérer n’importe quel type numérique. En réalité, la fonction attend un entier ou un objet qui fournit un index entier compatible. Un flottant comme 3.14 déclenche une erreur, et c’est normal: sa représentation binaire n’a rien à voir avec celle d’un entier. Je vois souvent cette confusion dans des scripts rapides où l’on mélange conversion numérique et affichage.
bin(3.14) # TypeError
bin('12') # TypeErrorLe deuxième piège concerne les nombres négatifs. Si vous cherchez une représentation sur 8, 16 ou 32 bits, bin(-5) n’est pas la bonne réponse. Pour obtenir une vue sur largeur fixe, il faut généralement masquer la valeur avec un AND bit à bit, par exemple avec 0xff pour 8 bits ou 0xffff pour 16 bits.
n = -5
format(n & 0xff, '08b') # '11111011'
format(n & 0xffff, '016b') # '1111111111111011'Ce détail est essentiel en réseau et en sécurité, parce qu’on raisonne souvent en octets, pas seulement en entiers signés. Le troisième piège, plus discret, consiste à oublier que 0b fait partie de la représentation texte, pas de la valeur. Si vous comparez des chaînes, sérialisez des logs ou alimentez une interface, il faut décider très tôt si vous voulez la forme complète ou les seuls bits. À partir de là, le choix de la bonne API dépend moins du goût que de l’usage réel.
Choisir la bonne méthode selon le besoin
Quand je travaille sur un projet un peu sérieux, je sépare toujours quatre usages différents: afficher, dimensionner, compter et transporter. Cette distinction évite beaucoup de code fragile. Voici le comparatif que j’utilise le plus souvent pour décider rapidement.
| Méthode | Ce qu’elle renvoie | Quand je la choisis |
|---|---|---|
bin(n) |
Une chaîne avec le préfixe 0b
|
Pour déboguer ou afficher rapidement une valeur |
format(n, 'b') |
Une chaîne binaire sans préfixe | Pour une sortie compacte ou une intégration dans un format texte |
f'{n:#b}' |
Une chaîne binaire avec préfixe, directement dans une f-string | Pour écrire du code lisible et garder le repère 0b
|
n.bit_length() |
Le nombre de bits nécessaires pour représenter la valeur absolue | Pour calculer une largeur, une taille de masque ou un padding |
n.bit_count() |
Le nombre de bits à 1 | Pour compter la densité de bits, par exemple sur des flags |
n.to_bytes(...) |
Un objet bytes
|
Pour encoder une valeur à transmettre ou stocker |
Les deux méthodes bit_length() et bit_count() ne servent pas à convertir en binaire au sens strict, mais elles deviennent vite indispensables dès qu’on manipule des bits de manière sérieuse. bit_length() aide à calculer une largeur minimale, par exemple pour construire un masque ou une chaîne zéro-remplie. bit_count(), lui, donne une information très utile pour les drapeaux, les signatures binaires et certaines vérifications de cohérence.
n = 19
n.bit_length() # 5, car 19 s'écrit 10011
n.bit_count() # 3, car il y a trois bits à 1Dès qu’il faut sortir du simple affichage, il faut aussi distinguer représentation texte et données binaires réelles, sinon on mélange deux mondes qui ne répondent pas aux mêmes règles.
Quand le binaire devient utile dans un projet réseau ou sécurité
Dans les outils réseau, les scripts d’audit ou les utilitaires de sécurité, le binaire sert souvent à lire des masques, des permissions, des flags ou des octets de protocole. Là, bin() reste utile pour voir ce qui se passe, mais ce n’est pas la bonne brique pour stocker ou transmettre la donnée. Si je dois construire un paquet, écrire un registre ou manipuler une valeur sur un nombre d’octets précis, je passe plutôt par to_bytes() ou par des opérations bit à bit.
value = 1024
value.to_bytes(2, byteorder='big') # b'\x04\x00'
value.to_bytes(2, byteorder='little') # b'\x00\x04'Cette différence compte parce qu’un entier et sa représentation sur octets ne racontent pas exactement la même histoire. bin() montre une forme lisible; bytes montre la réalité transportable. En pratique, je conseille de réserver bin() à l’inspection, et de confier la sérialisation aux primitives prévues pour cela. C’est encore plus vrai dès qu’un format externe, une API ou un protocole impose un ordre des octets, un signe ou une taille fixe.
Il y a aussi un cas fréquent en sécurité: l’inspection de bits de permission ou de masques de contrôle. Un affichage binaire permet de comprendre vite quels drapeaux sont actifs, mais la logique du programme doit rester en entier, pas en chaîne. Autrement dit, on lit en binaire, on calcule en entier, et on transporte en octets quand c’est nécessaire. Avant de terminer, je garde toujours une petite règle de décision qui m’évite les confusions entre représentation et transport.
Le réflexe que je garde avant d’écrire du binaire dans un vrai outil
-
Pour voir une valeur, j’utilise
bin()ouf'{n:#b}'. -
Pour une sortie propre, je préfère
format(n, 'b')ou une f-string équivalente. -
Pour une largeur fixe, j’ajoute un masque ou j’appuie le calcul sur
bit_length(). -
Pour compter les bits actifs, j’utilise
bit_count(). -
Pour encoder réellement la donnée, je passe à
to_bytes()ou à un module adapté au protocole.
Si je devais résumer l’idée en une seule ligne, je dirais: bin() sert à lire, format() sert à présenter, bit_length() sert à dimensionner, et to_bytes() sert à transporter. Cette séparation évite les erreurs les plus courantes et vous donne une base propre pour des scripts plus fiables, surtout dès qu’on touche au réseau, aux permissions ou aux formats binaires réels.