Magia de los números: ¿por qué 0.1 + 0.2 no es igual a 0.3?
¡Hola, futuro gurú de JavaScript! Si alguna vez intentaste sumar 0.1 y 0.2 en la consola del navegador, probablemente experimentaste un choque cultural:
console.log(0.1 + 0.2); // Muestra: 0.30000000000000004Sí, JavaScript no se ha vuelto loco. Este es un problema estándar de la aritmética de punto flotante (IEEE 754). Las computadoras simplemente no pueden representar perfectamente 0.1 en sistema binario; es como intentar escribir 1/3 en decimal. Resulta una fracción infinita.
Y aquí surge la pregunta clásica de Stack Overflow, que ha acumulado miles de visitas: «How to round to at most 2 decimal places, if necessary». ¿Cómo mostrar un número de forma limpia, sin la cola de «0000000004»? ¿Cómo redondear a dos decimales, pero solo si es necesario?
En este artículo analizaremos un tutorial paso a paso que resolverá el problema de una vez por todas. Aprenderás por qué toFixed() a veces miente, cómo funciona Math.round() y qué método elegir para una tienda online, una calculadora o simplemente para mostrar un precio de forma elegante.
Problema: «¡Quiero 2 decimales, pero no más!»
Imagina que estás programando un carrito de compras. El precio de un producto es 10.00. Quieres mostrar 10, no 10.00. Pero si el precio es 10.456, quieres mostrar 10.46. La lógica es simple: redondeamos a dos decimales, pero eliminamos los ceros innecesarios.
El método estándar toFixed(2) hace esto:
const price = 10;console.log(price.toFixed(2)); // "10.00" — pero necesitamos "10"
const price2 = 10.456;console.log(price2.toFixed(2)); // "10.46" — aquí está bienProblema: toFixed() siempre devuelve una cadena y siempre añade ceros. Esto no es adecuado.
El segundo detalle es que toFixed() puede redondear de forma extraña debido al mismo punto flotante:
console.log((1.005).toFixed(2)); // "1.00" — ¡pero debería ser "1.01"!¡Bingo! Por eso la pregunta en Stack Overflow es tan popular. Vamos a analizar las soluciones.
Solución n.º 1: Redondeo matemático (Math.round)
La forma más fiable es multiplicar el número por 100, redondear y dividir de nuevo. Funciona así:
function roundToTwo(num) { return Math.round(num * 100) / 100;}
console.log(roundToTwo(10.456)); // 10.46console.log(roundToTwo(10)); // 10console.log(roundToTwo(1.005)); // 1 — ups, problema otra vez¿Ves? 1.005 * 100 = 100.49999999999999 (debido a IEEE 754). Math.round redondea esto hacia abajo, da 100, dividimos entre 100 = 1. No está bien.
Conclusión: El método es bueno para casos simples, pero no para cálculos financieros. Para ejemplos de aprendizaje, sirve.
Solución n.º 2: Number.EPSILON — el salvador de la precisión
Para vencer el error de punto flotante, podemos añadir un delta diminuto: Number.EPSILON. Es la diferencia mínima entre dos números que JavaScript puede distinguir (aproximadamente 2.2e-16).
function roundToTwo(num) { return Math.round((num + Number.EPSILON) * 100) / 100;}
console.log(roundToTwo(1.005)); // 1.01 — ¡funciona!console.log(roundToTwo(10.456)); // 10.46console.log(roundToTwo(10)); // 10¿Por qué funciona? Empujamos el número ligeramente hacia arriba, para que 100.4999... se convierta en 100.5... y se redondee correctamente.
Importante: Este método no es perfecto para todos los casos extremos, pero para el 99% de las tareas es una excelente solución. De hecho, es lo que a menudo se recomienda en Stack Overflow.
Solución n.º 3: toFixed() + Number() — un truco astuto
Otra forma popular es usar toFixed(2) y luego convertir la cadena de nuevo a número mediante Number() o el operador unario +. Los ceros innecesarios se