- En ASCII,
Z est placé à 90 et a à 97, et les 6 caractères entre les deux font que l’écart de code entre majuscules et minuscules est exactement de 32
- Comme 32 vaut
2^5, deux lettres correspondantes comme A 65 et a 97 ne diffèrent toujours que d’un seul bit : 00100000
- Grâce à cette disposition, faire un AND avec le complément binaire de
32 met en majuscule, faire un OR avec 32 met en minuscule, et faire un XOR avec 32 inverse la casse
- On peut obtenir le rang alphabétique en faisant un AND avec
31 sur le code du caractère pour ne garder que les 5 bits de poids faible ; A/a vaut alors 1, et Z/z vaut 26
- ASCII est un ancien encodage de caractères en 7 bits qui ne représente que 128 points de code ; aujourd’hui, les 128 premiers points de code de Unicode sont identiques à ASCII
La disposition ASCII et l’écart de 32
- Dans la table ASCII, le code de la majuscule
Z vaut 90, et la minuscule a n’est pas placée juste après, mais à 97
- Entre les deux se trouvent les 6 caractères
[ \ ] ^ _ `
- L’alphabet anglais compte 26 lettres, et en ajoutant ces 6 caractères on obtient
26 + 6 = 32
32 correspond à 2^5, ce qui aligne la correspondance entre majuscules et minuscules sur la différence d’un bit précis
- Par exemple,
A vaut 65 soit 01000001, et a vaut 97 soit 01100001 ; la différence entre les deux est de 32
La relation entre ASCII et Unicode
- ASCII est l’un des premiers systèmes d’encodage de caractères ; il n’utilise que 7 bits pour représenter
2^7 = 128 points de code
- Ces 128 points de code ne suffisent pas à contenir tous les caractères utilisés par les humains, en particulier pour des langues comme le chinois qui comptent des dizaines de milliers de caractères
- Aujourd’hui, l’ensemble de caractères standard est Unicode, avec plusieurs encodages comme UTF-8 et UTF-16
- Les 128 premiers points de code d’Unicode sont identiques à ASCII
Le 5e bit qui sépare majuscules et minuscules
- Si l’on compare en binaire une majuscule et sa minuscule correspondante, c’est toujours le bit correspondant à
32 qui change
65 = 01000001 = A
97 = 01100001 = a
66 = 01000010 = B
98 = 01100010 = b
67 = 01000011 = C
99 = 01100011 = c
32 s’écrit 00100000 en binaire, et ce seul bit crée la différence entre majuscule et minuscule
- La disposition des lettres dans ASCII permet ainsi de transformer facilement la casse avec des opérations sur les bits
Traiter la casse avec des opérations binaires
-
Conversion en majuscules
- Pour transformer un caractère en majuscule, on effectue un AND binaire avec le complément de
32
0 1 1 0 0 0 0 1 (97 = 'a')
& 1 1 0 1 1 1 1 1 (mask)
-------------------
0 1 0 0 0 0 0 1 (65 = 'A')
- Appliqué à
a, 97 devient 65, donc A
- Si on applique la même opération à
A, il reste A
-
Conversion en minuscules
- Pour transformer un caractère en minuscule, on effectue un OR binaire avec
32
0 1 0 0 0 0 0 1 (65 = 'A')
| 0 0 1 0 0 0 0 0 (32)
-------------------
0 1 1 0 0 0 0 1 (97 = 'a')
- Appliqué à
A, 65 devient 97, donc a
- Si on applique la même opération à
a, il reste a
-
Inversion de casse
- Pour inverser la casse, on effectue un XOR binaire avec
32
0 1 1 0 0 0 0 1 (97 = 'a')
^ 0 0 1 0 0 0 0 0 (32)
-------------------
0 1 0 0 0 0 0 1 (65 = 'A')
0 1 0 0 0 0 0 1 (65 = 'A')
^ 0 0 1 0 0 0 0 0 (32)
-------------------
0 1 1 0 0 0 0 1 (97 = 'a')
a devient A, et A devient a
Obtenir le rang alphabétique avec les 5 bits de poids faible
- On peut obtenir le rang alphabétique en faisant un AND binaire avec
31 sur le code du caractère
0 1 0 0 0 0 0 1 (65 = 'A')
& 0 0 0 1 1 1 1 1 (31)
-------------------
0 0 0 0 0 0 0 1 (1)
0 1 1 1 1 0 1 0 (122 = 'z')
& 0 0 0 1 1 1 1 1 (31)
-------------------
0 0 0 1 1 0 1 0 (26)
31 s’écrit 00011111 en binaire ; il efface les bits de poids fort et ne conserve que les 5 bits de poids faible
- En ASCII, les 5 bits de poids faible des lettres correspondent exactement à leur position dans l’alphabet
A/a se termine par 00001 et vaut donc 1, B/b se termine par 00010 et vaut donc 2, et Z/z se termine par 11010 et vaut donc 26
- Dans les codes de caractères ASCII,
c & 31 est équivalent à c % 32
- Comme
32 est une puissance de 2, faire un masquage avec 31 supprime les blocs de taille 32 et ne conserve que le reste
'A' = 65 → 65 % 32 = 1
'B' = 66 → 66 % 32 = 2
...
'Z' = 90 → 90 % 32 = 26
'a' = 97 → 97 % 32 = 1
'b' = 98 → 98 % 32 = 2
...
'z' = 122 → 122 % 32 = 26
1 commentaires
Commentaires sur Lobste.rs
L’explication est correcte, mais j’ai l’impression que https://garbagecollected.org/2017/01/31/four-column-ascii/ l’explique mieux
Ce n’est pas seulement une question de Shift, Ctrl est aussi concerné. Par exemple, Tab est Ctrl-I, et comme I vaut
1001001, Ctrl masque le premier bit pour laisser0001001, soit TabSi l’on utilise une logique électromécanique plutôt qu’une logique électronique, comme les constructeurs des années 1960, un bit paired keyboard est bien plus simple à implémenter
La touche Shift n’a qu’à basculer un seul bit dans le caractère ASCII. Aujourd’hui, on met un CPU générique dans chaque clavier et la logique ne coûte pratiquement rien, mais à l’époque où les ordinateurs génériques occupaient une pièce entière, ce genre de solution coûtait bien plus cher
L’article présente comme motivation de la disposition ASCII le fait de pouvoir implémenter efficacement la conversion majuscules/minuscules par opérations sur les bits, mais cette valeur semble aujourd’hui assez mince, même si elle a pu compter historiquement
La conversion
a→Ane fonctionne que pour du texte simple, et même si ISO-8859-1 ou Unicode étendent partiellement cette disposition à des caractères commeüouç, la casse dépend du lieu, du contexte et de l’époque. La correspondance correcte en majuscules/minuscules pourßouïdépend de la langue, des autorités de normalisation, de la région d’usage et du moment historiqueUnicode fournit des tables de correspondance et de pliage de casse comme https://www.unicode.org/charts/case/ qui donnent une réponse assez proche pour les cas courants, mais comme des décisions humaines entrent en jeu, la complexité est en pratique infinie et il peut falloir un logiciel sur mesure pour obtenir le bon résultat
Aujourd’hui, si l’on ne peut pas garantir qu’on reste limité aux 2⁷ premiers points de code Unicode, l’offset
0b0010_0000casse facilement, et un chemin de code plus coûteux qu’une simple opération sur un bit devient inévitable. Même dans CPython,''.upperpasse par le chemin rapide ASCII deunicode_upper{,_impl}, mais en pratique il effectue une branche, une fonction inline, un accès à une structure, plusieurs fonctions et fonctions, des macros et finit par faire une consultation de tableLes implémentations de
''.upperdans les langages modernes ont en général une complexité comparable, et même si le compilateur optimise, l’approche moderne semble inévitablement plus coûteuse qu’une unique opération sur un bit. L’intérêt de cette conception a surtout des chances de ressortir dans des logiciels sur mesure, par exemple quand on fait des opérations arithmétiques sur des données binaires brutes ou sur unnumpy.ndarrayendtype=uint8contenant des données Unicode limitées à des points de code inférieurs ou égaux à 2⁷Ce qui m’intrigue, c’est ceci : si l’on suppose que la seule motivation de ce choix de conception est bien celle avancée dans l’article, il existait déjà à l’époque une alternative sous la forme d’une table de correspondance de 128 octets ; à quel moment de l’histoire de l’informatique une opération sur un seul bit a-t-elle été plus efficace ou plus réalisable qu’une consultation dans une table de 128 octets ?
Un choix de conception fait à un moment donné fige ensuite la forme de quelque chose en s’appuyant sur des hypothèses qui finiront par casser, rendant les extensions ultérieures difficiles voire impossibles. Des articles récents sur IPv6 montrent aussi que certains choix faits à l’époque, pertinents pour IPv4 et IPv6, sont aujourd’hui devenus un fardeau énorme
Dans une lecture bienveillante, ce genre de choix résulte d’un arbitrage entre risques et bénéfices. Quelque chose comme : « une simple opération sur un bit rend la conversion d’un caractère en majuscule bien plus rapide, donc cela vaut le risque qu’un jour ça casse quand des utilisateurs de japonais arriveront »
Vu depuis aujourd’hui, un choix fait en 1976 complique la vie en 2026 ; mais si l’on n’avait pas d’ordinateur en 1976, on n’a pas profité de l’avantage et il ne reste que les inconvénients. Si l’on met ce sentiment d’égoïsme de côté, je me demande comment mieux prévoir la durabilité de ce type de choix quand on conçoit un logiciel supposé rester populaire encore 20 ans plus tard
tolowerrapide au niveau bit reste utile. https://dotat.at/@/2022-06-27-tolower-swar.htmlAu moins en ASCII, les lettres restent contiguës. En EBCDIC, l’écart est de
0x40(64) et, comparé à l’ASCII, cela ressemble à deux lignes de 9 caractères et une ligne de 8 caractères empilées verticalementhttps://en.wikipedia.org/wiki/EBCDIC
Mieux vaut ne pas trop s’étendre sur le code 5 bits http://www.quadibloc.com/crypto/images/tele38.gif ni, si je me souviens bien, sur certains périphériques CDC qui utilisaient un code 6 bits rempli de caractères de contrôle de changement de page
Ça me rappelle que les pseudonymes IRC étaient insensibles à la casse ASCII, donc
foo{était identique àfoo[etbar|àbar\Je ne serais pas surpris que certains clients soient encore perturbés par ça