Electrónica Digital · Tema 26

Paridad y errores

Un cable largo, un poco de ruido, y un 1 se convierte en 0 sin avisar. La paridad agrega un solo bit que vigila a todos los demás: el XOR de la palabra completa. Detecta cualquier cantidad impar de errores y se rinde ante la par. En este tema vas a medir cuánto detecta, dónde falla, cómo se ve la misma idea en dos dimensiones (y entonces corrige) y por qué aparece en discos, GPS y puertos serie.

≈ 60 minutos Sin materiales: solo el navegador Incluye simulador y Python

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:

Las dos convenciones. «XOR» es el XOR de todos los bits de datos.
ConvenciónReglaPEl receptor espera
Paridad parTotal de unos parP = XORXOR de los N + 1 bits = 0
Paridad imparTotal de unos imparP = ~XORXOR de los N + 1 bits = 1
Tramas de ejemplo (datos + P).
DatosUnosP parTrama parP imparTrama impar
1011311011 101011 0
0110200110 010110 1
0000000000 010000 1
1111401111 011111 1
1011 0101511011 0101 101011 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:

Dos formas de conectar N − 1 XOR. Niveles = puertas en el camino más largo.
BitsXOREn cadena (niveles)En árbol (niveles)
8773
9884
1615154
3231315

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).

Funcionamiento del 74HC280 con sus 9 entradas A a I.
Unos en las 9 entradasΣPARΣIMPAR
0, 2, 4, 6 u 8 (par)10
1, 3, 5, 7 o 9 (impar)01
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.

Errores en una trama de 9 bits: cuántos patrones hay de cada tamaño y cuántos detecta la paridad.
Bits volteadosPatrones posiblesDetectados
199 (todos)
2360 (ninguno)
38484 (todos)
41260 (ninguno)
5126126 (todos)
911 (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.
¿Detecta la convención una línea pegada? Depende del largo de la trama.
TramaLínea en 0 (todos ceros)Línea en 1 (todos unos)
9 bits, paridad parpasa (0 unos, par)detecta (9 unos, impar)
9 bits, paridad impardetecta (0 unos, par)pasa (9 unos, impar)
8 bits, paridad parpasapasa (8 unos, par)
8 bits, paridad impardetectadetecta (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:

Un bloque de 4 × 4 con paridad par por fila y por columna. La última columna y la última fila son los bits de paridad.
FilaDatosP fila
01 0 1 11
10 1 1 00
21 1 0 00
30 0 1 10
P col0 0 1 01 (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() & 1 en Python o __builtin_parity en 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ó.
SIMULADOR 26

Canal ruidoso con paridad

10110101 + P=1 · sano

Receptor de paridad con árbol de XOR Nueve bits recibidos, de D7 a D0 y el bit de paridad, entran a un árbol de ocho puertas XOR de cuatro niveles. Los bits se pueden tocar para voltearlos como si el canal los hubiera dañado. El resultado final dice si la trama cumple la convención. NUEVE BITS RECIBIDOS, OCHO XOR Y UN VEREDICTO
Trama TX (datos + P)—
Trama RX (datos + P)—
Chequeo del receptor—
Errores reales y veredicto—

Trama sana: el XOR de los 9 bits cumple la convención.

Recorrido guiado (10 minutos)

  1. 1
    Mirá 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.

  2. 2
    Tocá 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.

  3. 3
    Tocá 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.

  4. 4
    Tocá un tercer bit.

    Con tres errores se detecta otra vez. Siempre manda la cuenta: impar se detecta, par se escapa.

  5. 5
    Apretá «Inyectar 1 error» varias veces.

    Siempre se detecta, pero el receptor nunca sabe cuál bit fue: tiene que pedir la trama otra vez.

  6. 6
    Apretá «Inyectar 2 errores» varias veces.

    Nunca se detectan, caigan donde caigan, incluso si uno de los dos es el bit de paridad.

  7. 7
    Cambiá 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.

  8. 8
    Con 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.

  9. 9
    Con 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.

  10. 10
    Poné 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

  1. Con 1 error, ¿qué se detecta y qué no puede decir el receptor?
  2. Con 2 errores, ¿hay alguna combinación que se detecte?
  3. ¿Qué convención detecta una línea pegada en 0 con 9 bits?
  4. Si el bit de paridad se voltea y los datos no, ¿se detecta?
Ver respuestas sugeridas
  1. Se detecta siempre (cantidad impar). Pero el receptor solo sabe que hay un error, no en qué bit: la paridad no localiza.
  2. Ninguna: dos bits volteados dejan la cantidad de unos con la misma paridad, sin importar cuáles sean (datos o paridad).
  3. La paridad impar: nueve ceros tienen cantidad par de unos y fallan la regla «total impar».
  4. 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

El mismo concepto, en dos mundos.
En softwareEn la paridad
Un dígito verificador de un documentoUn bit de redundancia que delata la corrupción
reduce(xor, bits) o x.bit_count() & 1El árbol de XOR del 74HC280
Un hash de un solo bitLa 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 nuevoEl único remedio de la paridad simple
Checksum XOR de NMEAParidad de 8 bits en paralelo, una por columna de bits
RAID 5: recuperar un disco con XORParidad usada cuando se sabe qué se perdió (borrado)
Una invariante con assertLa regla «total de unos par» que se comprueba a la llegada
ECC en la RAM de un servidorParidad 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
  1. 1
    Redundancia = 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é.

  2. 2
    Error 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.

  3. 3
    Má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.