- En ASCII,
Z está en 90 y a en 97; gracias a los 6 caracteres entre ambos, la diferencia de código entre mayúsculas y minúsculas queda en 32
- Como 32 es
2^5, las letras correspondientes, como A 65 y a 97, siempre difieren en un solo bit: 00100000
- Gracias a esta disposición, al hacer AND con el complemento de bits de
32 se obtiene la mayúscula, con OR con 32 se obtiene la minúscula, y con XOR con 32 se invierte entre mayúscula y minúscula
- El orden alfabético se puede obtener haciendo AND del código del carácter con
31 para dejar solo los 5 bits inferiores; así, A/a vale 1 y Z/z vale 26
- ASCII es una codificación de caracteres temprana de 7 bits que solo representa 128 puntos de código, y los primeros 128 puntos de código de Unicode que se usan hoy son idénticos a ASCII
La disposición de ASCII y la diferencia de 32
- En la tabla ASCII, el valor de código de la mayúscula
Z es 90, y la minúscula a no está en el valor siguiente, sino en 97
- Entre ambas están los 6 caracteres
[ \ ] ^ _ `
- El alfabeto inglés tiene 26 letras, y si se suman esos 6 caracteres,
26 + 6 = 32
- Como
32 corresponde a 2^5, la relación entre mayúsculas y minúsculas queda alineada para diferir en un solo bit específico
- Por ejemplo,
A es 65 y 01000001, mientras que a es 97 y 01100001; la diferencia entre ambos valores es 32
Relación entre ASCII y Unicode
- ASCII es uno de los primeros métodos de codificación de caracteres y usa solo 7 bits para representar
2^7 = 128 puntos de código
- Esos 128 puntos de código no alcanzan para contener todos los caracteres que usa la humanidad, y mucho menos idiomas como el chino, que tiene decenas de miles de caracteres
- Hoy en día se usa Unicode como conjunto de caracteres estándar, con varias codificaciones como UTF-8 y UTF-16
- Los primeros 128 puntos de código de Unicode son idénticos a ASCII
El quinto bit que separa mayúsculas y minúsculas
- Si se comparan en binario una mayúscula y su minúscula correspondiente, siempre cambia el bit que equivale a
32
65 = 01000001 = A
97 = 01100001 = a
66 = 01000010 = B
98 = 01100010 = b
67 = 01000011 = C
99 = 01100011 = c
32 en binario es 00100000, y ese único bit crea la diferencia entre mayúscula y minúscula
- La disposición del alfabeto en ASCII está hecha para que la conversión entre mayúsculas y minúsculas sea fácil mediante operaciones de bits
Manejar mayúsculas y minúsculas con operaciones de bits
-
Convertir a mayúscula
- Para convertir un carácter a mayúscula, se hace un AND de bits con el complemento de bits 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')
- Si se aplica a
a, 97 cambia a 65 y se convierte en A
- Si se aplica la misma operación a
A, que ya es mayúscula, permanece como A
-
Convertir a minúscula
- Para convertir un carácter a minúscula, se hace un OR de bits con
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')
- Si se aplica a
A, 65 cambia a 97 y se convierte en a
- Si se aplica la misma operación a
a, que ya es minúscula, permanece como a
-
Invertir mayúsculas y minúsculas
- Para invertir entre mayúscula y minúscula, se hace un XOR de bits con
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')
Obtener la posición alfabética con los 5 bits inferiores
- La posición alfabética se puede obtener haciendo un AND de bits del código del carácter con
31
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 en binario es 00011111, así que borra los bits de adelante y deja solo los 5 bits inferiores
- En ASCII, los 5 bits inferiores de los caracteres alfabéticos coinciden con su posición en el alfabeto
A/a termina en 00001, así que vale 1; B/b termina en 00010, así que vale 2; y Z/z termina en 11010, así que vale 26
- En los códigos de caracteres ASCII,
c & 31 es igual a c % 32
- Como
32 es una potencia de 2, al aplicar la máscara 31 se elimina el bloque de 32 al que pertenece y se conserva solo la parte restante
'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 comentarios
Comentarios de Lobste.rs
La explicación está bien, pero siento que https://garbagecollected.org/2017/01/31/four-column-ascii/ lo explica mejor.
No es solo un tema de Shift; Ctrl también está relacionado. Por ejemplo, Tab es Ctrl-I, porque I es
1001001y Ctrl enmascara el primer bit, dejando el0001001de TabSi usabas lógica electromecánica en vez de lógica electrónica, como muchos fabricantes en los años 60, un bit paired keyboard era mucho más fácil de implementar.
La tecla Shift solo tenía que alternar un bit del carácter ASCII. Hoy todos los teclados llevan una CPU de propósito general y la lógica sale prácticamente gratis, pero en la época en que una computadora de propósito general ocupaba una habitación entera, esa solución era muchísimo más cara
El artículo plantea como motivación del diseño de ASCII que la conversión entre mayúsculas y minúsculas podía implementarse eficientemente con operaciones de bits, pero su valor, aunque quizá importante históricamente, hoy parece bastante limitado.
La conversión
a→Asolo funciona con texto simple, y aunque ISO-8859-1 o Unicode extiendan parcialmente ese patrón a caracteres comoüoç, la conversión entre mayúsculas y minúsculas cambia según la región, el contexto y la época. La correspondencia correcta en mayúsculas/minúsculas paraßoïdepende del idioma, de organismos reguladores, del lugar de uso y del momento históricoUnicode ofrece tablas de mapeo y plegado de mayúsculas/minúsculas como https://www.unicode.org/charts/case/ que dan una respuesta bastante cercana para los casos comunes, pero como es un problema donde intervienen decisiones humanas, la complejidad es prácticamente infinita y puede requerir software a medida para hacerlo bien
Hoy, si no puedes garantizar que estarás limitado a los primeros 2⁷ code points de Unicode, el desplazamiento
0b0010_0000se rompe fácilmente, y es inevitable entrar en rutas de código más costosas que una sola operación de bit. Incluso en CPython,''.upperpasa por una ruta rápida ASCII enunicode_upper{,_impl}, pero en la práctica realiza una rama, una función inline, acceso a estructuras, varias funciones y funciones, macros y una consulta de tablaLa implementación de
''.upperen lenguajes modernos probablemente tenga un nivel de complejidad parecido, y aunque el compilador optimice, un enfoque moderno inevitablemente será más costoso que una sola operación de bit. La ventaja de este diseño probablemente solo destaque en software a medida, como hacer operaciones aritméticas sobre datos binarios puros o sobre unnumpy.ndarraycondtype=uint8que contenga datos Unicode limitados a code points menores o iguales a 2⁷Lo que me intriga es esto: si asumimos que la única motivación de esta decisión de diseño fue la que menciona el artículo, entonces incluso en ese momento existía la alternativa de una tabla de búsqueda de 128 bytes; ¿en qué período de la historia de la computación una sola operación de bit fue más eficiente o más viable que consultar una tabla de 128 bytes?
Una decisión de diseño tomada en cierto momento fija la forma de algo apoyándose en supuestos que después dejan de cumplirse, y hace que las expansiones futuras sean difíciles o imposibles. En artículos recientes sobre IPv6 también se ve cómo decisiones correctas en su momento para IPv4 e IPv6 terminaron siendo hoy una carga enorme
Viéndolo con benevolencia, estas decisiones debieron tomarse sopesando riesgos y beneficios. Algo como: “si convertir un carácter a mayúscula con una sola operación de bit es mucho más rápido, vale la pena aceptar el riesgo de que algún día se rompa cuando aparezcan usuarios de japonés”
Desde la perspectiva actual, una decisión de 1976 hace más difícil la vida en 2026, pero si en 1976 ni siquiera tenías una computadora, no disfrutaste esa ventaja y solo te quedaron las desventajas. Dejando de lado ese egoísmo, me interesa cómo podríamos predecir mejor la sostenibilidad de este tipo de decisiones cuando hacemos software que esperamos que siga siendo popular dentro de 20 años
tolowerrápido a nivel de bits todavía es útil. https://dotat.at/@/2022-06-27-tolower-swar.htmlAl menos en ASCII, las letras sí están en posiciones contiguas. EBCDIC tiene una separación de
0x40(64), y comparado con ASCII tiene la forma de dos filas de 9 caracteres y una de 8, apiladas verticalmentehttps://en.wikipedia.org/wiki/EBCDIC
Mejor ni hablar del código de 5 bits http://www.quadibloc.com/crypto/images/tele38.gif ni de ciertos periféricos CDC que, si no recuerdo mal, usaban un código de 6 bits lleno de caracteres de control para salto de página
Me acordé de cuando los nicknames de IRC eran ASCII sin distinción entre mayúsculas y minúsculas, así que
foo{era igual afoo[ybar|era igual abar\\No me sorprendería que algunos clientes todavía se confundan por eso