01 · Punto de partida
El bit que se dio vuelta solo
Mandás el byte 1011 0101 por un cable de varios metros, cerca de un motor. Un pico de ruido voltea un bit y llega 1011 0001. Ningún circuito de los que viste hasta ahora se entera: para el receptor, esa palabra es tan válida como cualquier otra. Para detectar el error hace falta redundancia: agregar información que permita comprobar que la palabra tiene sentido.
Una escena típica. Un sensor manda lecturas a un microcontrolador por un puerto serie. De vez en cuando llega una temperatura imposible. ¿Es el sensor o es el cable? Con un bit extra por byte, el receptor descarta las tramas corruptas y pide la lectura de nuevo, en lugar de actuar sobre un dato falso.
Es el mismo principio que el dígito verificador de un documento o el checksum de un archivo: un dato que no aporta información nueva, pero cuya incoherencia delata un problema. La paridad es la versión más barata que existe: un solo bit.
- ¿Cómo se calcula y se comprueba un bit de paridad?
- ¿Cuántos errores detecta, y cuáles se le escapan?
- ¿Cuánto cuesta en hardware, y qué hace el 74HC280?
- ¿Qué cambia si la línea queda pegada en 0 o en 1?
- ¿Cómo se consigue corregir con paridad?
- ¿Dónde la usa el mundo real?
02 · Paridad
El XOR de todos los bits
En el Tema 10 viste que el XOR de varios bits vale 1 cuando la cantidad de unos es impar. Eso es exactamente la paridad de una palabra. El transmisor agrega un bit P elegido para que el total de unos de la trama cumpla una convención acordada:
| Convención | Regla | P | El receptor espera |
|---|---|---|---|
| Paridad par | Total de unos par | P = XOR | XOR de los N + 1 bits = 0 |
| Paridad impar | Total de unos impar | P = ~XOR | XOR de los N + 1 bits = 1 |
| Datos | Unos | P par | Trama par | P impar | Trama impar |
|---|---|---|---|---|---|
| 1011 | 3 | 1 | 1011 1 | 0 | 1011 0 |
| 0110 | 2 | 0 | 0110 0 | 1 | 0110 1 |
| 0000 | 0 | 0 | 0000 0 | 1 | 0000 1 |
| 1111 | 4 | 0 | 1111 0 | 1 | 1111 1 |
| 1011 0101 | 5 | 1 | 1011 0101 1 | 0 | 1011 0101 0 |
- Por qué detecta un error
- Voltear un bit cambia la cantidad de unos en uno: de par pasa a impar. El XOR del receptor deja de dar el valor esperado. Con tres, cinco o cualquier cantidad impar de bits volteados pasa lo mismo.
- Distancia de Hamming
- La distancia entre dos palabras es la cantidad de bits en que difieren. Las tramas válidas de un código con paridad están siempre a distancia 2 o más unas de otras (cambiar un dato obliga a cambiar P). Un error cae a distancia 1 de una palabra válida: se nota, pero a esa distancia quedan varias palabras válidas, así que no se puede saber cuál era la original. Detecta, pero no corrige.
- Es lineal
- La paridad del XOR de dos palabras es el XOR de sus paridades. Por eso se puede calcular de a pedazos, en cualquier orden, o actualizar al cambiar un solo bit (basta voltear P).
03 · Hardware
Árbol de XOR y el 74HC280
Calcular la paridad de N bits son N − 1 puertas XOR, pero la forma en que se conectan cambia la velocidad:
| Bits | XOR | En cadena (niveles) | En árbol (niveles) |
|---|---|---|---|
| 8 | 7 | 7 | 3 |
| 9 | 8 | 8 | 4 |
| 16 | 15 | 15 | 4 |
| 32 | 31 | 31 | 5 |
Como el XOR es asociativo (Tema 10), se pueden agrupar de a pares: los resultados de la primera fila se vuelven a juntar de a pares, y así hasta quedar uno. La profundidad es el logaritmo en base 2 de N, redondeado hacia arriba. Es el mismo truco de balancear un árbol que usás al hacer una reducción paralela.
El 74HC280
El 74HC280 es un generador/verificador de paridad de 9 bits: ocho de datos más uno de paridad, justo un byte con su bit. Entrega dos salidas complementarias. Según la hoja de datos, ΣPAR vale 1 cuando la cantidad de entradas en 1 es par, y ΣIMPAR cuando es impar (confirmá nombres y pines en la hoja del fabricante).
| Unos en las 9 entradas | ΣPAR | ΣIMPAR |
|---|---|---|
| 0, 2, 4, 6 u 8 (par) | 1 | 0 |
| 1, 3, 5, 7 o 9 (impar) | 0 | 1 |
- Para generar P
- Los ocho datos van a las entradas A a H y la novena entrada (I) se ata a GND. Entonces ΣIMPAR vale 1 cuando los datos tienen una cantidad impar de unos: es el bit P de la paridad par. ΣPAR, en cambio, es el P de la paridad impar.
- Para verificar
- Los ocho datos y P (los nueve bits recibidos) van a las entradas. Con paridad par, la trama es sana si ΣPAR = 1 (total par); con paridad impar, si ΣIMPAR = 1.
- Entradas sobrantes
- Si se usan menos de 9, las libres se atan a GND: un 0 no cambia la paridad (nunca dejes una entrada flotando).
- Más de 9 bits
- Se encadenan chips: la salida de un 280 entra como un bit más a otro, porque la paridad de la paridad es la paridad del total.
04 · Límites
Qué detecta y qué no
Con una trama de 9 bits (8 de datos más P), se pueden probar todas las formas de voltear k bits. El resultado es limpio: se detecta exactamente cuando k es impar.
| Bits volteados | Patrones posibles | Detectados |
|---|---|---|
| 1 | 9 | 9 (todos) |
| 2 | 36 | 0 (ninguno) |
| 3 | 84 | 84 (todos) |
| 4 | 126 | 0 (ninguno) |
| 5 | 126 | 126 (todos) |
| 9 | 1 | 1 (todos) |
¿Cuánto importa en la práctica?
Si cada bit se voltea con una probabilidad p (la tasa de error del canal, BER), y los errores son independientes, la probabilidad de que lleguen errores en una cantidad impar se calcula con una fórmula cerrada. Con 9 bits y p = 10−3:
- Una trama de cada 112 (≈ 0,9 %) llega con un error o con una cantidad impar, y se detecta.
- Una trama de cada ~28 000 (≈ 0,004 %) llega con dos errores (o una cantidad par) y pasa sin detectar.
- Son unas 250 detecciones por cada falla silenciosa. La proporción es aproximadamente 2 / ((N − 1)·p): mejora cuando el canal es bueno (p chico) y empeora cuando el ruido sube.
- Los errores no son independientes
- El ruido real suele llegar en ráfagas: un pico de varios bits seguidos. Una ráfaga de longitud par pasa tan fácil como dos errores. Para ráfagas se usan códigos más fuertes (el CRC, que no se ve en este curso).
- No dice dónde
- La paridad solo avisa «algo anda mal». La corrección es reintentar (como hace un protocolo con retransmisión) o agregar más redundancia (sección siguiente).
- Líneas pegadas
- Un cable cortado o un transmisor apagado deja la línea en 0 (o en 1) todo el tiempo. Una trama de nueve ceros tiene cantidad par de unos: pasa el chequeo de paridad par. Una de nueve unos (cantidad impar) pasa el de paridad impar. La convención elegida decide qué falla se nota.
| Trama | Línea en 0 (todos ceros) | Línea en 1 (todos unos) |
|---|---|---|
| 9 bits, paridad par | pasa (0 unos, par) | detecta (9 unos, impar) |
| 9 bits, paridad impar | detecta (0 unos, par) | pasa (9 unos, impar) |
| 8 bits, paridad par | pasa | pasa (8 unos, par) |
| 8 bits, paridad impar | detecta | detecta (8 unos, par) |
05 · Paridad en dos dimensiones
Un bit por fila y uno por columna
Si en lugar de una palabra se transmite un bloque, se puede agregar un bit de paridad a cada fila y otro a cada columna (más uno en la esquina). Con un bloque de 4 × 4 datos, el arreglo completo queda de 5 × 5:
| Fila | Datos | P fila |
|---|---|---|
| 0 | 1 0 1 1 | 1 |
| 1 | 0 1 1 0 | 0 |
| 2 | 1 1 0 0 | 0 |
| 3 | 0 0 1 1 | 0 |
| P col | 0 0 1 0 | 1 (esquina) |
Cada columna de datos suma: col 0 = 1 + 0 + 1 + 0 = 2 (par), col 1 = 0 + 1 + 1 + 0 = 2, col 2 = 1 + 1 + 0 + 1 = 3 (impar, P = 1), col 3 = 1 + 0 + 0 + 1 = 2. Y la esquina cierra la fila de paridades.
- Un error se localiza y se corrige
- Si se voltea un solo bit, falla una fila y una columna: el cruce de las dos es la celda dañada. Se invierte y listo. Funciona incluso si el bit dañado es uno de los de paridad.
- Dos errores se detectan
- Quedan dos filas malas, o dos columnas malas (o ambas): hay un problema, pero no se puede decir cuáles son las celdas. No se intenta corregir.
- Tres errores se detectan (pero pueden corregirse mal)
- El síndrome nunca es cero con 1, 2 o 3 errores. Pero con tres errores en forma de «L» el patrón de filas y columnas malas se parece al de un solo error, y la corrección «arregla» la celda equivocada. Es el límite de este esquema: corrige 1, detecta hasta 3, pero no distingue 1 de ciertos 3.
- Cuatro errores pueden pasar
- Cuatro errores en las esquinas de un rectángulo dejan todas las filas y columnas pares: sin síntoma. En un arreglo de 5 × 5 hay exactamente 100 rectángulos, de las 12 650 formas de elegir 4 celdas.
- El costo
- Para 4 × 4 son 9 bits extra por 16 de datos (más de la mitad). Crece el bloque, baja la proporción: para 8 × 8 son 17 por 64.
Es el primer paso hacia los códigos de corrección. El código de Hamming que viste en el Tema 10 hace lo mismo con muchos menos bits, eligiendo qué subconjuntos de bits verifica cada paridad.
06 · Aplicaciones
UART, discos y checksums
- Puerto serie (UART)
- Un byte con paridad par se escribe 8E1: 8 bits de datos, Even, 1 bit de parada. En la línea viajan 11 bits: bit de inicio, los 8 de datos (el menos significativo primero), el de paridad y el de parada. Además de la detección, el receptor verifica el bit de parada (error de trama). 8N1 es la versión sin paridad, la más común hoy.
- Memorias
- Los PC antiguos usaban módulos de memoria de 9 bits por byte (el noveno era la paridad). Hoy los servidores usan ECC: códigos de Hamming ampliados con un bit de paridad global, que corrigen un error y detectan dos.
- Discos en RAID 5
- Con tres discos de datos se guarda un cuarto con el XOR de los tres. Si un disco falla, su contenido es el XOR de los otros y la paridad. Es la paridad usada de otra forma: cuando se sabe cuál se perdió, la redundancia no solo detecta, también recupera (Paso 6).
- Checksum de las sentencias NMEA
- Los módulos GPS terminan cada línea con un asterisco y dos dígitos hexadecimales: el XOR de todos los caracteres anteriores. Es una paridad de 8 bits en paralelo, una por columna. Detecta cualquier cantidad impar de errores en una misma columna de bits, pero no ve dos caracteres intercambiados, porque el XOR no depende del orden (Paso 6).
- En software
x.bit_count() & 1en Python o__builtin_parityen GCC calculan la paridad de una palabra: muchas CPU la entregan como un flag tras cada operación.
07 · Simulador
Laboratorio: canal ruidoso
El simulador transmite un byte con su bit de paridad. Elegís los 8 bits de datos y la convención (par o impar); el transmisor calcula P. Después podés dañar la trama: tocando cualquiera de los 9 bits del lado del receptor, con los botones de error aleatorio (1, 2 o 3 bits) o con una línea pegada en 0 o en 1. A la derecha ves el árbol de XOR que usa el receptor para verificar.
- Círculos de la izquierda: los 9 bits recibidos (D7 a D0 y P). Verde si valen 1, gris si valen 0, y con anillo naranja si fueron volteados en el canal.
- Árbol: ocho XOR en cuatro niveles; cada nodo muestra su valor. La paridad no dice dónde está el error, solo que el resultado final cambió.
- Resultados: las tramas transmitida y recibida, el chequeo, y si el error fue detectado o se coló.
Canal ruidoso con paridad
10110101 + P=1 · sano
Trama sana: el XOR de los 9 bits cumple la convención.
Recorrido guiado (10 minutos)
- 1Mirá el estado inicial.
Datos 10110101 (cinco unos, cantidad impar), así que con paridad par P = 1 y la trama tiene seis unos. El XOR de los nueve bits recibidos da 0: sano.
- 2Tocá un bit de datos del lado del receptor (por ejemplo D3).
Ahora hay un error. La cantidad de unos pasó a impar y el XOR final da 1: detectado. Los nodos del árbol por los que pasa ese bit cambian de valor, y los demás no.
- 3Tocá un segundo bit cualquiera.
Dos errores: el XOR vuelve a 0 y el chequeo dice «OK». Es un falso negativo: la trama está dañada y el receptor lo ignora. Es el límite de la paridad.
- 4Tocá un tercer bit.
Con tres errores se detecta otra vez. Siempre manda la cuenta: impar se detecta, par se escapa.
- 5Apretá «Inyectar 1 error» varias veces.
Siempre se detecta, pero el receptor nunca sabe cuál bit fue: tiene que pedir la trama otra vez.
- 6Apretá «Inyectar 2 errores» varias veces.
Nunca se detectan, caigan donde caigan, incluso si uno de los dos es el bit de paridad.
- 7Cambiá a «Paridad impar» y mirá P.
Con los mismos datos, P pasa de 1 a 0, y el receptor ahora espera un XOR total de 1. Mezclar convenciones entre emisor y receptor marcaría todo como error.
- 8Con paridad par, apretá «Línea pegada en 0».
El receptor ve nueve ceros: cantidad par de unos, pasa. Con paridad impar, la misma falla se detecta.
- 9Con paridad par, apretá «Línea pegada en 1».
Nueve unos: cantidad impar, se detecta. Con paridad impar es al revés: pasa. La convención decide qué falla de cable se nota.
- 10Poné los 8 datos en 0 y volvé a la línea pegada en 0.
La trama transmitida ya era 000000000: la línea pegada no produce ningún error, porque coincide con lo que se mandaba. Ninguna paridad puede ver una falla que no cambia los datos.
Preguntas para pensar
- Con 1 error, ¿qué se detecta y qué no puede decir el receptor?
- Con 2 errores, ¿hay alguna combinación que se detecte?
- ¿Qué convención detecta una línea pegada en 0 con 9 bits?
- Si el bit de paridad se voltea y los datos no, ¿se detecta?
Ver respuestas sugeridas
- Se detecta siempre (cantidad impar). Pero el receptor solo sabe que hay un error, no en qué bit: la paridad no localiza.
- Ninguna: dos bits volteados dejan la cantidad de unos con la misma paridad, sin importar cuáles sean (datos o paridad).
- La paridad impar: nueve ceros tienen cantidad par de unos y fallan la regla «total impar».
- Sí: es un error de un solo bit. El dato llegó bien, pero el receptor lo marca como erróneo (falsa alarma sobre los datos). La paridad no distingue en qué bit está el problema.
08 · Paridad en código
Generar, medir, pegar líneas, corregir en 2D y recuperar discos
Los bloques generan y verifican paridad par e impar, miden qué detecta (probando todos los patrones de error y con probabilidades exactas), muestran qué convención nota una línea pegada, implementan el código en dos dimensiones con corrección, calculan la paridad con un árbol de XOR, reconstruyen un disco perdido y calculan un checksum NMEA.
Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.
Paso 1: generar y verificar
from functools import reduce
from itertools import product
from operator import xor
def paridad(bits):
return reduce(xor, bits, 0)
def trama(datos, convencion="par"):
p = paridad(datos)
return list(datos) + [p if convencion == "par" else 1 - p]
def sana(rx, convencion="par"):
return paridad(rx) == (0 if convencion == "par" else 1)
datos = [1, 0, 1, 1, 0, 1, 0, 1]
for conv in ("par", "impar"):
t = trama(datos, conv)
print(f"{conv:5s}: datos={datos} P={t[-1]} unos en la trama={sum(t)} chequeo ok: {sana(t, conv)}")
pb = lambda x: bin(x).count("1") % 2
print()
print("bit_count equivale al XOR de los bits (256 bytes):",
all(pb(x) == paridad([(x >> i) & 1 for i in range(8)]) for x in range(256)))
print("es lineal: paridad(a xor b) = paridad(a) xor paridad(b):",
all(pb(a ^ b) == pb(a) ^ pb(b) for a in range(256) for b in range(256)))
palabras = [tuple(trama(d)) for d in product((0, 1), repeat=4)]
dist = min(sum(x != y for x, y in zip(u, v)) for i, u in enumerate(palabras) for v in palabras[i + 1:])
print("distancia mínima entre tramas válidas (4 datos + P):", dist)
Con paridad par P = 1 (cinco unos en los datos) y la trama tiene 6 unos; con paridad impar P = 0 y tiene 5. El XOR de los bits equivale a bit_count y es lineal. La distancia mínima entre tramas válidas es 2: por eso detecta un error y no puede corregirlo.
Paso 2: cadena frente a árbol de XOR
import random
def cadena(bits):
acc, niveles = bits[0], 0
for b in bits[1:]:
acc ^= b
niveles += 1
return acc, niveles
def arbol(bits):
nivel, niveles = list(bits), 0
while len(nivel) > 1:
siguiente = [nivel[i] ^ nivel[i + 1] for i in range(0, len(nivel) - 1, 2)]
if len(nivel) % 2:
siguiente.append(nivel[-1])
nivel, niveles = siguiente, niveles + 1
return nivel[0], niveles
rnd = random.Random(3)
ok = True
for _ in range(2000):
n = rnd.randint(2, 40)
bits = [rnd.getrandbits(1) for _ in range(n)]
ok = ok and cadena(bits)[0] == arbol(bits)[0]
print("cadena y árbol dan siempre el mismo resultado:", ok)
print()
print("bits XOR niveles en cadena niveles en árbol")
for n in (8, 9, 16, 32, 64):
bits = [1] * n
print(f"{n:4d} {n - 1:3d} {cadena(bits)[1]:17d} {arbol(bits)[1]:16d}")
Cadena y árbol calculan lo mismo con las mismas N − 1 puertas, pero el árbol tiene log₂ N niveles en lugar de N − 1: 4 niveles para los 9 bits del 74HC280, 6 para 64.
Paso 3: qué detecta, probado y calculado
from functools import reduce
from itertools import combinations
from math import comb
from operator import xor
N = 9
paridad = lambda bits: reduce(xor, bits, 0)
original = [1, 0, 1, 1, 0, 1, 0, 1]
t = original + [paridad(original)] # trama con paridad par
print("errores patrones detectados")
regla = True
for k in range(N + 1):
total = detectados = 0
for pos in combinations(range(N), k):
r = list(t)
for i in pos:
r[i] ^= 1
total += 1
detectados += 0 if paridad(r) == 0 else 1
regla = regla and (detectados == (total if k % 2 else 0))
print(f"{k:7d} {total:8d} {detectados:10d}")
print("se detecta exactamente cuando la cantidad de errores es impar:", regla)
def p_impar(n, p):
return (1 - (1 - 2 * p) ** n) / 2
def p_par_sin_cero(n, p):
return (1 + (1 - 2 * p) ** n) / 2 - (1 - p) ** n
print()
print(" BER detectada sin detectar proporción coincide con la suma directa")
for p in (1e-2, 1e-3, 1e-4):
directo_det = sum(comb(N, k) * p ** k * (1 - p) ** (N - k) for k in range(1, N + 1, 2))
directo_no = sum(comb(N, k) * p ** k * (1 - p) ** (N - k) for k in range(2, N + 1, 2))
a, b = p_impar(N, p), p_par_sin_cero(N, p)
coincide = abs(a - directo_det) < 1e-12 and abs(b - directo_no) < 1e-12
print(f"{p:7.0e} {a:.3e} {b:.3e} {a / b:8.0f} {coincide}")
Con k errores en 9 bits se detectan todos los patrones si k es impar y ninguno si es par (regla: True). Con p = 10⁻³: ≈ 0,9 % de las tramas llegan con una cantidad impar de errores (se detectan) y ≈ 0,004 % pasan con una cantidad par. La proporción ronda 250 y crece cuando el canal mejora.
Paso 4: líneas pegadas y convención
from functools import reduce
from operator import xor
paridad = lambda bits: reduce(xor, bits, 0)
def sana(rx, convencion):
return paridad(rx) == (0 if convencion == "par" else 1)
print("largo convención todo ceros todo unos")
for n in (8, 9):
for conv in ("par", "impar"):
ceros = "pasa" if sana([0] * n, conv) else "detecta"
unos = "pasa" if sana([1] * n, conv) else "detecta"
print(f"{n:5d} {conv:10s} {ceros:10s} {unos}")
print()
print("Con 8 datos + P (9 bits): ¿qué convención nota cada falla de cable?")
print(" línea en 0 -> impar; línea en 1 -> par")
Con 9 bits (8 de datos y P), paridad par deja pasar «todo ceros» pero detecta «todo unos», y la impar al revés. Con 8 bits (7 de datos y P), los unos son una cantidad par y la paridad par deja pasar las dos fallas. Ninguna convención es mejor en general: depende de qué falla es más probable y del largo de la trama.
Paso 5: paridad en dos dimensiones
from itertools import combinations
F = C = 4
def codificar(datos):
m = [list(f) + [sum(f) % 2] for f in datos]
m.append([sum(m[r][c] for r in range(F)) % 2 for c in range(C + 1)])
return m
def sindrome(m):
filas = [r for r in range(F + 1) if sum(m[r]) % 2]
cols = [c for c in range(C + 1) if sum(m[r][c] for r in range(F + 1)) % 2]
return filas, cols
def voltear(m, celdas):
m = [list(f) for f in m]
for r, c in celdas:
m[r][c] ^= 1
return m
def corregir(m):
filas, cols = sindrome(m)
if not filas and not cols:
return "sano", m
if len(filas) == 1 and len(cols) == 1:
return f"corrige la celda ({filas[0]},{cols[0]})", voltear(m, [(filas[0], cols[0])])
return "error detectado, no corregible", m
datos = [[1, 0, 1, 1], [0, 1, 1, 0], [1, 1, 0, 0], [0, 0, 1, 1]]
m = codificar(datos)
celdas = [(r, c) for r in range(F + 1) for c in range(C + 1)]
for f in m:
print(*f)
print()
print("bloque original sano:", sindrome(m) == ([], []))
print("los 25 errores simples se corrigen:",
all(corregir(voltear(m, [x]))[1] == m for x in celdas))
print("1, 2 y 3 errores nunca dan síndrome cero (siempre se detectan):",
all(sindrome(voltear(m, s)) != ([], []) for k in (1, 2, 3) for s in combinations(celdas, k)))
sin_ver = sum(1 for s in combinations(celdas, 4) if sindrome(voltear(m, s)) == ([], []))
total4 = sum(1 for _ in combinations(celdas, 4))
print(f"con 4 errores, {sin_ver} de {total4} combinaciones pasan sin síntoma (los rectángulos)")
print()
dos = corregir(voltear(m, [(0, 0), (2, 3)]))
print("dos errores:", dos[0])
tres = corregir(voltear(m, [(0, 0), (0, 1), (1, 0)]))
print("tres errores en L:", tres[0], "-> ¿queda igual al original?", tres[1] == m)
print()
for n in (4, 8, 16):
print(f"bloque {n}x{n}: {(n + 1) ** 2 - n * n} bits de paridad por {n * n} de datos "
f"({100 * ((n + 1) ** 2 - n * n) / (n * n):.1f} %)")
Cualquier error simple se corrige, incluso en las celdas de paridad. Con 1, 2 o 3 errores el síndrome nunca es cero, y con 4 errores 100 de las 12 650 combinaciones (los rectángulos) pasan sin síntoma. Dos errores se detectan sin corregir; tres errores en «L» se corrigen mal, parecen un solo error. El costo en bits baja al crecer el bloque.
Paso 6: discos recuperados y un checksum XOR
from functools import reduce
from operator import xor
discos = [0x3C, 0xA5, 0x0F]
paridad_disco = reduce(xor, discos)
print(f"bloques: {[hex(d) for d in discos]} paridad = {paridad_disco:#04x}")
perdido = 1
restantes = [d for i, d in enumerate(discos) if i != perdido]
recuperado = reduce(xor, restantes, paridad_disco)
print(f"se pierde el disco {perdido}: XOR del resto y de la paridad = {recuperado:#04x} (original {discos[perdido]:#04x})")
def checksum_nmea(cuerpo):
return reduce(xor, (ord(ch) for ch in cuerpo), 0)
cuerpo = "GPGLL,4916.45,N,12311.12,W,225444,A"
cs = checksum_nmea(cuerpo)
print()
print(f"${cuerpo}*{cs:02X}")
intercambiado = cuerpo.replace("4916", "4961") # dos dígitos intercambiados
un_digito = cuerpo.replace("4916", "4917") # un dígito cambiado
print("un dígito cambiado: ", "pasa" if checksum_nmea(un_digito) == cs else "se detecta")
print("dos dígitos intercambiados:", "pasa" if checksum_nmea(intercambiado) == cs else "se detecta")
Con la paridad XOR de tres discos se recupera el disco perdido (0xA5). El checksum NMEA detecta un dígito cambiado, pero no dos intercambiados: el XOR no depende del orden, igual que la paridad no ve dos errores.
09 · Analogía de programador
Un checksum de un bit
| En software | En la paridad |
|---|---|
| Un dígito verificador de un documento | Un bit de redundancia que delata la corrupción |
reduce(xor, bits) o x.bit_count() & 1 | El árbol de XOR del 74HC280 |
| Un hash de un solo bit | La paridad: detecta algunos cambios, pero tiene colisiones evidentes |
| Dos bugs que se tapan entre sí | Dos errores que se cancelan en el XOR |
| Retransmisión (TCP): detectar, descartar y pedir de nuevo | El único remedio de la paridad simple |
| Checksum XOR de NMEA | Paridad de 8 bits en paralelo, una por columna de bits |
| RAID 5: recuperar un disco con XOR | Paridad usada cuando se sabe qué se perdió (borrado) |
Una invariante con assert | La regla «total de unos par» que se comprueba a la llegada |
| ECC en la RAM de un servidor | Paridad cruzada con más estructura: corrige uno y detecta dos |
| Protocolo con paridad configurable (par, impar, ninguna) | La convención compartida de los dos extremos |
- 1Redundancia = invariante.
Un dato que no agrega información pero que debe cumplir una regla. Cuando la regla se rompe, se sabe que algo pasó, aunque no qué.
- 2Error frente a borrado.
Si sabés cuál se perdió (un disco que falló), la paridad recupera el dato. Si solo sabés que algo cambió, solo la detecta. Dos usos de la misma redundancia.
- 3Más redundancia, más poder.
Un bit detecta uno. Dos dimensiones corrigen uno y detectan hasta tres. Hamming y CRC hacen lo mismo con más matemática. Siempre hay un compromiso entre bits extra y garantías.
Una diferencia con el software: un hash de 32 bits tiene una probabilidad de colisión de uno en 4 000 millones para cambios al azar. La paridad de un bit tiene una probabilidad de 1 en 2 de no ver un cambio aleatorio fuerte, aunque sí caza siempre los errores simples, que son los más comunes en un canal bueno.
10 · Comprensión
Confusiones frecuentes
«La paridad corrige errores»
Solo detecta una cantidad impar de bits volteados y no dice cuál. Corregir pide más redundancia (paridad en dos dimensiones, Hamming) o reintentar.
«Dos errores se detectan, que son más graves»
Es lo contrario: se cancelan y pasan. La paridad es ciega a cualquier cantidad par de errores, por eso en un canal con ráfagas se necesitan códigos más fuertes.
«Par o impar da exactamente lo mismo»
Los dos detectan lo mismo en errores de bits, pero ambos extremos tienen que usar la misma convención, y detectan distinto una línea pegada: con 9 bits, la par deja pasar «todo ceros» y la impar deja pasar «todo unos».
«El bit de paridad viaja por otro lado»
Viaja con los datos, como un bit más de la trama (el noveno de un byte). Sin él el receptor no puede comprobar nada, y hay que contarlo en el ancho de banda: un byte con 8E1 ocupa 11 bits en la línea.
«El 74HC280 da la paridad en una sola salida»
Entrega dos, complementarias: ΣPAR vale 1 con cantidad par de unos y ΣIMPAR con cantidad impar. Hay que elegir la correcta según la convención y según se genere o se verifique.
«Un checksum XOR detecta cualquier cambio»
No ve dos caracteres intercambiados, porque el XOR no depende del orden, ni dos errores en la misma columna de bits de dos caracteres. Es una paridad por columna, con los mismos límites.
«Con paridad en dos dimensiones, tres errores se corrigen»
Se detectan, pero tres errores en forma de «L» producen el mismo síntoma que un error simple y la corrección «arregla» la celda equivocada. Corrige uno y detecta más, pero no distingue siempre.
11 · Práctica guiada
Ejercicios de vigilancia
Ejercicio 1: tramas a mano
Para los datos 0110, ¿cuánto vale P con paridad par y con impar? ¿Cómo son las tramas? Comprobá con el XOR.
Ver solución razonada
0110 tiene dos unos (par). Paridad par: P = 0, trama 0110 0 (el total sigue siendo dos). Paridad impar: P = 1, trama 0110 1 (tres unos). El XOR de la trama par da 0; el de la impar da 1.
Ejercicio 2: el byte 0x41 en 8E1
Un puerto serie envía la letra «A» (0x41 = 0100 0001) en 8E1. ¿Cuánto vale P? ¿Qué bits viajan por la línea, en orden? ¿Qué parte de la trama es overhead?
Ver solución razonada
0x41 tiene dos unos: P = 0 con paridad par. Orden en la línea: inicio (0), los datos del menos significativo al más: 1, 0, 0, 0, 0, 0, 1, 0, la paridad 0 y la parada (1): 11 bits. Overhead: 3 bits de cada 11 (inicio, paridad y parada), el 27 %; la paridad sola es 1 bit por 8 de datos (12,5 %).
Ejercicio 3: el 74HC280
Con cuatro interruptores en las entradas A a D y las demás atadas a GND, ¿para qué valores de 0 a 15 vale 1 la salida ΣIMPAR? ¿Y ΣPAR?
Ver solución razonada
ΣIMPAR vale 1 con cantidad impar de unos: 1, 2, 4, 7, 8, 11, 13 y 14 (la mitad exacta). ΣPAR vale 1 en los otros ocho: 0, 3, 5, 6, 9, 10, 12 y 15. Las entradas sobrantes en GND valen 0 y no cambian la paridad. Verificalo con el Paso 1.
Ejercicio 4: localizar un error en 2D
Se recibe este bloque con paridad par por fila y por columna (la última fila y la última columna son paridades). ¿Dónde está el error?
1 0 1 0
0 1 0 0
1 1 0 0
0 0 0 0
Ver solución razonada
Sumas de filas: 2, 1, 2, 0: falla la fila 1 (cuenta desde 0). Sumas de columnas: 2, 2, 1, 0: falla la columna 2. El cruce es la celda (1, 2): un 0 que debería ser 1. Corregida, la fila 1 queda 0 1 1 | 0, que suma 2.
Ejercicio 5: cuánto se escapa
Una trama de 9 bits cruza un canal con p = 10−3 por bit. ¿Aproximadamente qué fracción de las tramas llega con dos errores (y pasa sin detectar)? ¿Una de cada cuántas?
Ver solución razonada
Dos errores entre 9 bits: C(9, 2) · p² · (1 − p)7 ≈ 36 · 10−6 ≈ 3,6 · 10−5, es decir, una de cada ~28 000 tramas. Frente a eso, ≈ 0,9 % de las tramas llegan con un solo error y se detectan: unas 250 veces más. El Paso 3 lo calcula exactamente.
Ejercicio 6: recuperar un disco
Tres discos guardan los bytes 0x3C, 0xA5 y 0x0F, y un cuarto guarda su XOR. ¿Cuánto vale la paridad? Si falla el disco del medio, ¿cómo se recupera su byte?
Ver solución razonada
0x3C ⊕ 0xA5 = 0x99, y 0x99 ⊕ 0x0F = 0x96. Si falla el disco de 0xA5: 0x3C ⊕ 0x0F = 0x33, y 0x33 ⊕ 0x96 = 0xA5, el byte original. El XOR de todos los bloques (paridad incluida) siempre vale 0, así que cualquier bloque es el XOR de los demás.
12 · Síntesis
Ideas para recordar
- Paridad par: P = XOR de los datos; el total de unos de la trama es par y el receptor espera XOR = 0. Con paridad impar, P = ~XOR y el receptor espera 1.
- Detecta exactamente una cantidad impar de bits volteados; una cantidad par se escapa. No localiza, no corrige: la distancia mínima entre tramas válidas es 2.
- Con p = 10⁻³ y 9 bits, por cada trama que pasa dañada sin detectar se detectan unas 250. La ventaja crece cuando el canal mejora, y se pierde con ráfagas.
- Hardware: N − 1 XOR; en árbol bastan log₂ N niveles. El 74HC280 verifica 9 bits y entrega ΣPAR y ΣIMPAR (ΣPAR = 1 con cantidad par de unos).
- Una línea pegada en 0 o en 1 se nota o no según la convención y el largo de la trama: con 9 bits, la par ve «todo unos» y la impar ve «todo ceros».
- Paridad en dos dimensiones: corrige un error (cruce de la fila y la columna malas), detecta hasta tres, pero tres errores en «L» se corrigen mal y cuatro en un rectángulo pasan.
- Aplicaciones: UART (8E1: 11 bits en la línea), memorias, RAID 5 (recuperar con XOR cuando se sabe qué se perdió) y el checksum XOR de NMEA (ciego al orden).
- Ambos extremos tienen que usar la misma convención: detectar y corregir son cosas distintas, y más redundancia da más poder.
¿Listo para el Tema 27?
Podés avanzar si sos capaz de responder esto sin mirar:
- Calcular el bit de paridad par e impar de un byte y verificar una trama recibida.
- Explicar por qué detecta una cantidad impar de errores y no una par.
- Decir qué entrega cada salida del 74HC280 y cómo se usa para generar y verificar.
- Indicar qué convención detecta una línea pegada en 0 con 9 bits.
- Localizar y corregir un error simple en un bloque con paridad por filas y columnas.
- Explicar cómo se recupera un disco perdido con XOR.
Ya sabés sumar, comparar y vigilar. Con esas piezas se arma la primera máquina que hace todo junto: en el próximo tema, la ALU elemental, un solo bloque combinacional que suma, resta, compara y opera bit a bit según un código de operación.