Números de punto flotante — Por qué 0.1 + 1.1 no es 1.2
Al finalizar este tema
Podrá responder técnicamente a la pregunta: "¿Por qué 0.1 + 0.2 es 0.30000000000000004?".
Este código le sorprenderá
print(0.1 + 0.2)# 0.30000000000000004console.log(0.1 + 0.2);
// 0.30000000000000004En Python, JavaScript, Java o C, se obtiene el mismo resultado en todos los lenguajes de programación. No es un error; es una limitación fundamental de la forma en que las computadoras almacenan los números decimales.
¿Por qué ocurre esto?
Las computadoras almacenan todo en binario (0 y 1). Los números enteros se convierten con precisión:
13 = 1101₂ (exacto)Sin embargo, los números decimales son distintos. Al convertir 0.1 a binario:
0.1 = 0.0001100110011001100110011... ₂ (repetición infinita)Al igual que en el sistema decimal 1/3 = 0.33333... es un decimal periódico, en el sistema binario 0.1 también es un decimal periódico. Dado que las computadoras no pueden almacenar números infinitos, deben truncarlos en algún punto. Este "truncamiento" genera errores.
Casos en los que el problema surge realmente en la práctica.
# Cálculo del precioprice = 0.1 + 0.1 + 0.1print(price == 0.3) # False!
# Acumulación repetidatotal = 0.0for _ in range(10): total += 0.1print(total) # 0.9999999999999999 (¡no es 1.0!)Si en una tienda en línea la suma de diez artículos de 100 wones da como resultado 999 wones, el cliente presentará una queja. En los sistemas financieros, cualquier discrepancia, incluso de 1 won, provoca una auditoría.
Algunos números son exactos
No todos los números presentan errores:
print(0.5 + 0.25) # 0.75 (¡exacto!)print(0.125 + 0.0625) # 0.1875 (¡exacto!)0.5 = 1/2, 0.25 = 1/4, 0.125 = 1/8: estos valores pueden expresarse como sumas de potencias de 2 y, por lo tanto, se convierten con precisión a binario. Por el contrario, 0.1 = 1/10 no puede representarse mediante potencias de 2, lo que genera un error de precisión.
Solución
1. Convertir a enteros para calcular — la opción más práctica
# Calcular en centavos en lugar de unidades monetariasprice_in_cents = 10 + 10 + 10 # 30 centavostotal = price_in_cents / 100 # 0.3 unidades monetarias (exacto)2. Tolerancia de error al comparar
# ❌ Nunca comparar de esta formaif a == 0.3:
# ✅ Comparar dentro de un margen de errorif abs(a - 0.3) < 1e-9: print("Se consideran iguales")
# Python 3.5+import mathif math.isclose(a, 0.3): print("Se consideran iguales")3. Módulo Decimal — cuando la precisión es absolutamente crítica.
from decimal import Decimal
a = Decimal("0.1") + Decimal("0.2")print(a) # 0.3 (¡exacto!)print(a == Decimal("0.3")) # TrueDebe crearse como la cadena "0.1". No tiene sentido asignar a Decimal(0.1) un valor que ya contiene un error de punto flotante.
IEEE 754 — El estándar para almacenar números decimales
La razón por la que todos los lenguajes de programación producen el mismo error es que siguen el estándar internacional IEEE 754. Este estándar divide los 64 bits (doble precisión) en tres partes:
[1 bit de signo] [11 bits de exponente] [52 bits de mantisa]Un mantisa de 52 bits puede representar aproximadamente entre 15 y 17 dígitos significativos. Dado que la representación binaria de 0,1 es un número periódico infinito, al truncarse a 52 bits, el resultado es aproximadamente 0.1000000000000000055511151231257827021181583404541015625.
Lo mismo ocurre en otros lenguajes
// Java
System.out.println(0.1 + 0.2); // 0.30000000000000004
// C
printf("%.17f\n", 0.1 + 0.2); // 0.30000000000000004
// Go
fmt.Println(0.1 + 0.2 == 0.3) // falseEn JavaScript, todos los números son IEEE 754 de doble precisión, por lo que los enteros también se ven afectados:
console.log(9007199254740992 + 1); // 9007199254740992 (¡sin cambios!)
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991Para superar esta limitación, se utiliza BigInt.
Lista de verificación práctica
| Situación | Solución |
|---|---|
| Comparación de decimales | math.isclose() o abs(a-b) < 1e-9 |
| Cálculos monetarios | Decimal o unidades enteras (céntimos, etc.) |
| Cálculos numéricos de gran volumen | float tal cual (el error es despreciable) |
| Serialización JSON | round(value, 10) antes de guardar |
| Base de datos | Utilizar el tipo DECIMAL |
Punto clave: Que una computadora cometa errores en cálculos decimales no es un error de software, sino una limitación fundamental del sistema binario. Para cálculos monetarios, use Decimal o unidades enteras; para comparaciones, use
math.isclose(). Solo necesita recordar estos dos puntos.