Number en JavaScript: precisión, límites y cálculos confiables
En JavaScript, number parece un tipo sencillo: sumas, restas, multiplicas y divides. Pero en cuanto trabajas con dinero, porcentajes, IDs grandes, datos de formularios o cálculos que deben ser exactos, aparecen detalles importantes. Entender cómo funciona Number evita bugs silenciosos y decisiones técnicas equivocadas.
Un solo tipo para enteros y decimales
JavaScript usa un único tipo number para representar enteros y decimales:
console.log(typeof 42); // "number"
console.log(typeof 3.14); // "number"
console.log(3 === 3.0); // true
Internamente, los números usan el formato IEEE 754 de 64 bits. Esto permite trabajar con un rango enorme de valores, pero también introduce límites de precisión.
El problema clásico: 0.1 + 0.2
Los decimales no siempre pueden representarse de forma exacta en binario:
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
Esto no es un bug de JavaScript; es una consecuencia de cómo funcionan los números de punto flotante. Para comparar decimales, usa una tolerancia:
function nearlyEqual(a, b) {
return Math.abs(a - b) < Number.EPSILON;
}
console.log(nearlyEqual(0.1 + 0.2, 0.3)); // true
Para dinero, normalmente conviene trabajar en unidades enteras. Por ejemplo, guardar centavos en lugar de pesos o dólares:
const priceInCents = 1299;
const quantity = 3;
const totalInCents = priceInCents * quantity;
console.log(totalInCents); // 3897
NaN e Infinity
JavaScript incluye valores numéricos especiales:
console.log(10 / 0); // Infinity
console.log(-10 / 0); // -Infinity
console.log(Number('hello')); // NaN
NaN significa “Not a Number”, pero su comportamiento puede confundir:
console.log(NaN === NaN); // false
console.log(Number.isNaN(NaN)); // true
Usa Number.isNaN() para comprobar si un valor es realmente NaN. Evita el isNaN() global cuando puedas, porque primero convierte el valor y puede dar resultados inesperados.
Parsear números desde strings
Los datos de un input llegan como texto. Para convertirlos:
const age = Number('25');
const price = Number.parseFloat('19.99');
const page = Number.parseInt('12', 10);
Number() exige que todo el string sea numérico:
console.log(Number('12px')); // NaN
console.log(Number.parseInt('12px', 10)); // 12
Esa diferencia es útil, pero también peligrosa. Si estás validando formularios, normalmente quieres detectar entradas inválidas en lugar de aceptar un número parcial.
Redondeo y formato
toFixed() devuelve un string, no un number:
const value = 12.345;
const rounded = value.toFixed(2);
console.log(rounded); // "12.35"
console.log(typeof rounded); // "string"
Para mostrar moneda o números con formato local, usa Intl.NumberFormat:
const formatter = new Intl.NumberFormat('es-AR', {
style: 'currency',
currency: 'ARS',
});
console.log(formatter.format(1299.5));
Separar cálculo y presentación mantiene el código más sano: calcula con números, muestra con formateadores.
Límites seguros
JavaScript puede representar números muy grandes, pero no todos los enteros grandes son seguros:
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
console.log(Number.isSafeInteger(9007199254740992)); // false
Si un ID viene de una base de datos y supera ese límite, no lo trates como number. Guárdalo como string o usa BigInt si necesitas operar matemáticamente con él.
BigInt
BigInt permite representar enteros más grandes que el límite seguro:
const huge = 123456789012345678901234567890n;
console.log(typeof huge); // "bigint"
Pero no puedes mezclar number y bigint directamente:
const count = 10n;
// count + 1; // TypeError
console.log(count + 1n); // 11n
Úsalo para enteros grandes, no para decimales ni dinero con centavos.
Math para operaciones comunes
El objeto Math incluye utilidades frecuentes:
console.log(Math.round(4.6)); // 5
console.log(Math.floor(4.6)); // 4
console.log(Math.ceil(4.1)); // 5
console.log(Math.max(3, 9, 1)); // 9
Math.random() sirve para valores aleatorios simples, pero no para seguridad, tokens o criptografía. Para eso existe crypto.getRandomValues().
Buenas prácticas
- Convierte strings a números de forma explícita.
- Valida con
Number.isNaN()yNumber.isFinite(). - No compares decimales exactos si hubo operaciones de punto flotante.
- Para dinero, usa enteros menores, como centavos.
- No trates IDs gigantes como
number; usa string oBigInt. - Usa
Intl.NumberFormatpara presentar números al usuario.
Conclusión
Number es suficiente para la mayoría de cálculos cotidianos, pero no es mágico. Saber dónde pierde precisión, cómo validar entradas y cuándo usar BigInt o enteros para dinero te ayuda a escribir software más confiable. Los bugs numéricos suelen ser silenciosos, así que conviene prevenirlos desde el diseño.