Aritmética numérica · Tema 27

Sumación computacional confiable

Por qué una suma finita puede depender del orden en una computadora y cómo reducir la pérdida de información mediante algoritmos compensados y por pares.

01 · Dos modelos

La suma real y la suma de la máquina

En aritmética real, una suma finita puede reagruparse y reordenarse sin cambiar su valor. En una computadora, la mayoría de los números reales se reemplazan por aproximaciones y cada operación vuelve a redondear el resultado.

Si fl(x) representa el valor almacenado de x, el cálculo efectivo es una secuencia como:

sk=fl(sk−1+ak)

El redondeo intermedio modifica la entrada de la operación siguiente. Por eso importan tanto el orden como el algoritmo de acumulación.

02 · Representación

Qué almacena un Number de JavaScript

El tipo Number utiliza normalmente el formato binario de doble precisión IEEE 754. Un valor finito se representa mediante signo, exponente y una significación de 53 bits de precisión efectiva.

Esto permite un rango enorme, pero solo alrededor de 16 cifras decimales significativas. Números como 0.1 no tienen representación binaria finita:

console.log({
  suma: 0.1 + 0.2,
  igualdadExacta: 0.1 + 0.2 === 0.3,
  epsilon: Number.EPSILON,
  enteroSeguroMaximo: Number.MAX_SAFE_INTEGER
});

El resultado no indica un fallo del lenguaje: es la consecuencia esperada de representar una recta continua con una cantidad finita de patrones binarios.

03 · Precisión relativa

La distancia entre números cambia con la escala

Number.EPSILON=2⁻⁵² es la separación entre 1 y el siguiente número representable mayor que 1. No es una tolerancia universal.

Cerca de una magnitud |x|, el espaciado es aproximadamente proporcional a:

ulp(x) ≈ |x|·2−52

Alrededor de 10¹⁶, la separación ya es del orden de unidades. Entonces sumar 1 puede no alterar el acumulador:

1016+1 se almacena como 1016

La precisión es relativa a la escala, no una cantidad fija de decimales después de la coma.

04 · Propiedad perdida

La suma en coma flotante no es asociativa

const a = 1e16;
const b = -1e16;
const c = 1;

console.log({
  izquierda: (a + b) + c,
  derecha: a + (b + c),
  exacto: 1
});

En el primer orden, a+b se cancela exactamente y queda 1. En el segundo, b+c se redondea a b; luego a+b produce 0.

Ambas expresiones son idénticas en los reales, pero describen secuencias distintas de redondeos.

05 · Pérdida de cifras

Cancelación y condicionamiento

Restar dos cantidades cercanas puede eliminar sus cifras principales y dejar expuestos los errores presentes en las cifras menores. Este fenómeno se denomina cancelación sustractiva.

Hay que distinguir dos cuestiones:

Problema mal condicionado
Pequeños cambios en los datos exactos causan grandes cambios relativos en el resultado.
Algoritmo inestable
El procedimiento introduce o amplifica más error del necesario para ese problema.

Ningún algoritmo de suma puede recuperar información que ya se perdió al construir los datos, pero sí puede evitar perder información adicional durante la acumulación.

06 · Método básico

La acumulación de izquierda a derecha

function sumaIngenua(valores) {
  let suma = 0;
  for (const valor of valores) suma += valor;
  return suma;
}

const datos = [1e16, 1, 1, 1, -1e16];
console.log({ ingenua: sumaIngenua(datos), exacta: 3 });

Este algoritmo usa tiempo lineal, memoria constante y suele ser suficiente cuando los términos tienen escalas similares y no existe una cancelación extrema.

Su error depende del orden. En el peor caso, los redondeos de muchas operaciones pueden acumularse aproximadamente en proporción a la cantidad de sumandos.

07 · Estrategia simple

Sumar primero las magnitudes pequeñas

Para términos no negativos, ordenar por valor absoluto creciente suele mejorar la precisión. Los términos pequeños se combinan entre sí antes de enfrentarse a un acumulador grande que podría absorberlos.

|a1|≤|a2|≤···≤|an|

Sin embargo, ordenar cuesta aproximadamente O(n log n), modifica el orden original y no constituye una solución universal cuando hay signos y cancelaciones.

08 · Árbol de reducción

Sumación por pares

En lugar de formar una cadena larga, la suma por pares combina valores en un árbol equilibrado. Cada resultado atraviesa solo unas log₂n etapas de redondeo.

(a₁+a₂)+(a₃+a₄)+··· → sumar resultados por pares

El trabajo total sigue siendo O(n), mientras que la profundidad se reduce de n a O(log n). Además, la estructura se presta a procesamiento paralelo.

function sumaPorPares(valores) {
  if (valores.length === 0) return 0;
  let nivel = valores.slice();
  while (nivel.length > 1) {
    const siguiente = [];
    for (let i = 0; i < nivel.length; i += 2) {
      siguiente.push(i + 1 < nivel.length
        ? nivel[i] + nivel[i + 1]
        : nivel[i]);
    }
    nivel = siguiente;
  }
  return nivel[0];
}

09 · Suma compensada

El algoritmo de Kahan

Kahan mantiene una compensación c que aproxima los bits de baja magnitud perdidos en la suma anterior. Antes de agregar el término siguiente, devuelve esa corrección al cálculo.

function sumaKahan(valores) {
  let suma = 0;
  let compensacion = 0;
  for (const valor of valores) {
    const corregido = valor - compensacion;
    const siguiente = suma + corregido;
    compensacion = (siguiente - suma) - corregido;
    suma = siguiente;
  }
  return suma;
}

La resta final reconstruye el error local de redondeo bajo el modelo habitual. El costo es unas pocas operaciones adicionales por término y memoria constante.

10 · Interpretación

Qué representa la compensación

Si suma es tan grande que una parte de corregido desaparece al calcular siguiente, entonces:

(siguiente−suma)−corregido

estima esa diferencia. En la iteración posterior se resta la compensación —que posee el signo adecuado— del nuevo valor.

La compensación no es una «memoria exacta» de todo el error ni garantiza el resultado correctamente redondeado en cualquier secuencia. Es una corrección eficaz bajo condiciones comunes y mejora drásticamente muchas sumas de términos pequeños sobre un acumulador grande.

11 · Compensación reforzada

La variante de Neumaier

Kahan puede comportarse mal cuando el próximo sumando tiene mayor magnitud que el acumulador. Neumaier decide qué operando domina y acumula explícitamente la parte pequeña.

function sumaNeumaier(valores) {
  let suma = 0;
  let compensacion = 0;
  for (const valor of valores) {
    const siguiente = suma + valor;
    if (Math.abs(suma) >= Math.abs(valor)) {
      compensacion += (suma - siguiente) + valor;
    } else {
      compensacion += (valor - siguiente) + suma;
    }
    suma = siguiente;
  }
  return suma + compensacion;
}

La suma final incorpora la corrección acumulada. Sigue usando tiempo lineal y memoria constante, a cambio de más operaciones y una comparación por elemento.

12 · Elección práctica

Costos y fortalezas

Comparación cualitativa de métodos.
MétodoTiempoMemoria extraUso característico
IngenuoO(n)O(1)Datos benignos o baja exigencia
Ordenado por magnitudO(n log n)Depende del ordenamientoTérminos no negativos con gran rango
Por paresO(n)O(n) en la versión simpleReducciones grandes o paralelas
KahanO(n)O(1)Flujos secuenciales con incrementos pequeños
NeumaierO(n)O(1)Signos y magnitudes muy variables

No hay un ganador absoluto. La elección depende de precisión, rendimiento, memoria, paralelismo y estructura de los datos.

13 · Caso resuelto

Incrementos absorbidos por una magnitud grande

Consideremos 1e16, mil valores iguales a 1 y finalmente −1e16. La suma real es 1000.

const valores = [1e16, ...Array(1000).fill(1), -1e16];

console.log({
  ingenua: valores.reduce((s, x) => s + x, 0),
  kahan: sumaKahan(valores),
  neumaier: sumaNeumaier(valores),
  exacta: 1000
});

La suma ingenua puede perder cada unidad mientras el acumulador vale 1e16. Los métodos compensados conservan, con distinto grado de robustez, información que el acumulador principal no pudo representar.

14 · Antes de sumar

Reformular puede ser mejor que compensar

La primera defensa numérica consiste en evitar una expresión inestable. Por ejemplo, para x pequeño, calcular:

√(1+x)−1

provoca cancelación. Racionalizar produce la forma equivalente:

x/[√(1+x)+1]

que conserva mejor las cifras significativas. De forma similar, Math.expm1(x) evalúa eˣ−1 y Math.log1p(x) evalúa ln(1+x) con precisión para x cercano a cero.

Un buen algoritmo de suma no compensa una fórmula previa mal condicionada si existe una identidad algebraica estable.

15 · Rango finito

Desbordamiento y subdesbordamiento

La precisión no es el único límite. Una operación puede producir Infinity aunque el resultado final exacto sea finito, o puede redondear valores extremadamente pequeños a cero.

console.log({
  desbordaAntesDeCancelar: Number.MAX_VALUE * 2 - Number.MAX_VALUE * 2,
  minimoPositivo: Number.MIN_VALUE,
  mitadDelMinimo: Number.MIN_VALUE / 2
});

Reordenar, factorizar una escala común o trabajar con logaritmos puede evitar valores intermedios fuera del rango. Una vez que aparece Infinity o un cero por subdesbordamiento, la suma compensada no recupera la información.

16 · Sumas infinitas truncadas

Combinar error analítico y error numérico

Al aproximar una serie mediante Sₙ intervienen dos desviaciones:

|S−fl(SN)| ≤ |S−SN| + |SN−fl(SN)|

El primer término es el error de truncamiento estudiado en el tema anterior. El segundo es el error de acumulación en coma flotante.

Una estrategia completa asigna parte del presupuesto de error a cada componente. No tiene sentido exigir un resto inferior a 10⁻²⁰ si la representación y el algoritmo solo sostienen una precisión absoluta de 10⁻¹⁵ en esa escala.

17 · Criterio operativo

Cuando el término deja de cambiar la suma

La condición suma + término === suma detecta estancamiento numérico, no convergencia matemática. Puede ocurrir porque el término es menor que media unidad de espaciado del acumulador, aunque la cola contenga millones de términos del mismo signo.

Una parada confiable debería combinar:

  • una cota matemática para el resto;
  • una tolerancia absoluta y/o relativa bien definida;
  • un límite de iteraciones como medida de seguridad;
  • una advertencia si la suma se estanca antes de alcanzar la cota.

El estancamiento es información diagnóstica: puede indicar que hace falta otra fórmula, una escala diferente o mayor precisión.

18 · Pruebas numéricas

Cómo evaluar un sumador

Una prueba útil incluye datos adversos con resultado conocido, no solo entradas aleatorias. Conviene ensayar:

  • magnitudes iguales y signos opuestos;
  • un número grande rodeado de muchos incrementos pequeños;
  • permutaciones de los mismos datos;
  • cantidades positivas con gran rango dinámico;
  • vectores vacíos, un solo elemento, ceros e infinitos según el contrato.

Para sumas de enteros construidas dentro de un rango controlado, BigInt puede proporcionar una referencia exacta. Para datos reales generales se necesita aritmética de mayor precisión o una biblioteca especializada.

19 · Exploración interactiva

Laboratorio: cinco formas de acumular

Elegí un patrón adverso y modificá su tamaño. Las barras muestran el error absoluto de cada algoritmo respecto de una suma exacta conocida.

EXPERIMENTO 27

Errores de sumación

10¹⁶ + 1 + ··· + 1 − 10¹⁶

Resultados debajo.
Suma exacta1000
Ingenua0
Kahan1000
Neumaier1000

Las unidades pueden desaparecer mientras el acumulador vale 10¹⁶.

Las barras comparan suma ingenua, orden inverso, suma por pares, Kahan y Neumaier; una barra mínima indica error cero.

20 · Comprensión

Confusiones frecuentes

«Number.EPSILON es el error de cualquier operación»

No. Describe el espaciado relativo cerca de 1; el error absoluto depende de la escala y de las operaciones.

«La suma compensada siempre devuelve el valor exacto»

No. Reduce mucho ciertos errores, pero sigue operando con precisión finita.

«Ordenar siempre mejora una suma con signos»

No necesariamente. La cancelación y el patrón de signos pueden hacer preferible otra agrupación o un método compensado.

«Si dos órdenes producen resultados distintos, la serie es condicional»

No. Una suma finita puede variar por redondeo; la sensibilidad matemática de una serie condicional es otro fenómeno.

«Más términos siempre dan una mejor aproximación»

Disminuyen el truncamiento de una serie convergente, pero también agregan operaciones y error de redondeo.

«Kahan reemplaza una fórmula numéricamente estable»

No. Primero conviene reformular expresiones inestables; después se elige cómo acumular.

21 · Práctica y síntesis

Ejercicios de precisión

Ejercicio 1: asociatividad

Predecí los resultados de (1e16 + 1) - 1e16 y 1e16 + (1 - 1e16) en JavaScript.

Ver solución

El primero suele producir 0 porque 1e16+1 se redondea a 1e16. El segundo también produce 0 en doble precisión para estos valores. En cambio, (1e16-1e16)+1 produce 1.

Ejercicio 2: ordenar positivos

¿Por qué sumar [1e16, 1, 1, 1] en orden inverso puede ser mejor?

Ver solución

Las tres unidades se combinan primero y forman 3, una cantidad con más posibilidades de sobrevivir al sumarse a 1e16. Aun así, el resultado almacenado queda limitado por el espaciado alrededor de 1e16.

Ejercicio 3: elegir algoritmo

Se reciben millones de mediciones en flujo y no pueden almacenarse. ¿Qué método resulta razonable?

Ver solución

Kahan o Neumaier: ambos son lineales y requieren memoria constante. Neumaier suele ser más robusto si cambian mucho los signos y magnitudes.

Ejercicio 4: dos errores

Explicá por qué una cota del resto de 10⁻¹² no garantiza por sí sola doce decimales correctos.

Ver solución

La cota controla el truncamiento exacto. Falta considerar la representación de los términos, el redondeo durante la suma y la escala del resultado.

Ejercicio 5: parada

¿Qué informa la igualdad suma + término === suma?

Ver solución

Que ese término no cambia el número representable almacenado en la escala actual. No demuestra que los términos restantes sumen una cantidad despreciable.

Ideas para recordar

  • La aritmética de coma flotante redondea después de cada operación.
  • La suma finita deja de ser asociativa en la máquina.
  • El espaciado entre números representables aumenta con la magnitud.
  • La suma por pares reduce la profundidad de acumulación y facilita el paralelismo.
  • Kahan conserva una corrección local; Neumaier refuerza el manejo de magnitudes variables.
  • Reformular una expresión inestable puede aportar más que cambiar el sumador.
  • En una serie truncada deben presupuestarse tanto el resto como el error numérico.
  • Los casos adversos con resultado conocido son esenciales para validar una implementación.

En el próximo tema volveremos a introducir una variable en la suma y estudiaremos las series de potencias como representaciones de funciones.