1 puntos por GN⁺ 4 시간 전 | 1 comentarios | Compartir por WhatsApp
  • 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')
  • a pasa a A, y A pasa a 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

 
GN⁺ 4 시간 전
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 1001001 y Ctrl enmascara el primer bit, dejando el 0001001 de Tab

  • Si 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→A solo 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órico
    Unicode 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_0000 se 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, ''.upper pasa por una ruta rápida ASCII en unicode_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 tabla
    La implementación de ''.upper en 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 un numpy.ndarray con dtype=uint8 que 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?

    • Cuando uno se mete a fondo en tecnologías populares que duraron mucho tiempo, este tipo de trucos de bits aparece seguido.
      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
    • Hay muchos protocolos de red que usan codificación ASCII sin distinción entre mayúsculas y minúsculas, así que un tolower rápido a nivel de bits todavía es útil. https://dotat.at/@/2022-06-27-tolower-swar.html
  • Al 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 verticalmente
    https://en.wikipedia.org/wiki/EBCDIC

  • Me acordé de cuando los nicknames de IRC eran ASCII sin distinción entre mayúsculas y minúsculas, así que foo{ era igual a foo[ y bar| era igual a bar\\
    No me sorprendería que algunos clientes todavía se confundan por eso