Objeto `Math`

Este artículo explica sobre el objeto Math.

Explica paso a paso, desde el uso básico hasta patrones prácticos comunes, así como trampas y sus soluciones.

YouTube Video

Objeto Math

El objeto Math de JavaScript proporciona un conjunto de utilidades integradas para cálculos numéricos.

Constantes

Las constantes definidas en Math son útiles al tratar con valores de alta precisión.

El siguiente código muestra algunas constantes típicas.

1// Show common Math constants
2console.log("Math.PI:", Math.PI);         // 3.141592653589793
3console.log("Math.E:", Math.E);           // Euler's number
4console.log("Math.LN2:", Math.LN2);       // Natural log of 2
5console.log("Math.SQRT2:", Math.SQRT2);   // Square root of 2
  • Estas constantes pueden usarse tal cual para funciones trigonométricas, conversiones logarítmicas, normalización, y más. Por ejemplo, Math.PI es necesario para convertir ángulos a radianes.

Redondeo básico y valores absolutos (abs, floor, ceil, round, trunc)

El redondeo y el manejo de signos se utilizan frecuentemente, por lo que es importante comprender correctamente las diferencias entre Math.floor, Math.ceil y Math.round. Especialmente al manejar números negativos, los resultados pueden diferir de la intuición, por lo que es necesario tener cuidado con cómo funciona cada regla de redondeo y usarlas adecuadamente.

Los siguientes ejemplos ilustran las diferencias entre las funciones de redondeo.

 1// Examples of rounding and absolute value functions
 2const pos = 3.7;
 3const neg = -3.7;
 4
 5// --- floor ---
 6console.log("Math.floor(3.7):", Math.floor(pos));  // 3
 7console.log("Math.floor(-3.7):", Math.floor(neg)); // -4
 8// (rounds toward smaller value)
 9
10// --- ceil ---
11console.log("Math.ceil(3.7):", Math.ceil(pos));  // 4
12console.log("Math.ceil(-3.7):", Math.ceil(neg)); // -3
13// (rounds toward larger value)
14
15// --- round ---
16console.log("Math.round(3.5):", Math.round(3.5));   // 4
17// (.5 goes up for positive numbers)
18console.log("Math.round(-3.5):", Math.round(-3.5)); // -3
19// (.5 moves toward zero for negative numbers)
20
21// --- trunc ---
22console.log("Math.trunc(3.7):", Math.trunc(pos));  // 3
23console.log("Math.trunc(-3.7):", Math.trunc(neg)); // -3
24// (fraction removed toward zero)
25
26// --- abs ---
27console.log("Math.abs(-3.7):", Math.abs(neg)); // 3.7
  • Math.floor redondea hacia el número menor para números negativos, así que -3.7 se convierte en -4.
  • Math.ceil redondea hacia el número mayor para números negativos, así que -3.7 se convierte en -3.
  • Math.round redondea .5 hacia arriba para números positivos, y para números negativos, redondea hacia cero.
  • Los números negativos pueden producir resultados que difieren de la intuición, por lo que es importante comprender claramente en qué dirección se están redondeando los números.
  • Las funciones de redondeo deben seleccionarse según el caso de uso. Por ejemplo, use Math.floor para cálculos de índices, Math.ceil para límites superiores, y Math.trunc cuando solo quiera eliminar la parte decimal.

Multiplicación, exponenciación y raíces (pow, **, sqrt, cbrt, hypot)

La potenciación y las raíces cuadradas se pueden hacer con Math.pow o con el operador **. Math.hypot calcula de forma segura la raíz cuadrada de la suma de los cuadrados (distancia).

A continuación se muestran ejemplos de exponenciación y hypot. Math.hypot mitiga el impacto del desbordamiento (overflow) y el subdesbordamiento (underflow).

1// Power, root and hypot examples
2console.log("2 ** 10:", 2 ** 10);                // 1024
3console.log("Math.pow(2, 10):", Math.pow(2, 10));// 1024
4console.log("Math.sqrt(16):", Math.sqrt(16));    // 4
5console.log("Math.cbrt(27):", Math.cbrt(27));    // 3
6console.log("Math.hypot(3, 4):", Math.hypot(3, 4));// 5 (3-4-5 triangle)
  • Math.hypot es útil al tratar con lados de triángulos o longitudes de vectores multidimensionales.

Funciones exponenciales y logarítmicas (exp, log, log10, log2)

Los exponentes y logaritmos se usan frecuentemente en probabilidad, decaimiento exponencial y escalado.

A continuación, se presentan ejemplos básicos de exp y logaritmos. Asegúrese de usar la función apropiada, según la base del logaritmo.

1// Exponential and logarithm examples
2console.log("Math.exp(1):", Math.exp(1));     // e^1
3console.log("Math.log(Math.E):", Math.log(Math.E)); // natural log, 1
4console.log("Math.log10(100):", Math.log10(100));   // 2
5console.log("Math.log2(8):", Math.log2(8));         // 3
  • Los logaritmos naturales (Math.log) son estándar en estadística y ecuaciones diferenciales. Los logaritmos decimales y de base 2 también se eligen según la aplicación.

Funciones trigonométricas (sin, cos, tan, asin, acos, atan2)

Las funciones trigonométricas son esenciales para los cálculos de ángulos, rotaciones y transformaciones de coordenadas. Math.atan2(y, x) es útil para calcular ángulos considerando correctamente los cuadrantes.

A continuación están los ejemplos básicos y el uso de atan2. Los ángulos deben manejarse en radianes.

 1// Trigonometry examples (angles in radians)
 2const degToRad = deg => deg * (Math.PI / 180);
 3const radToDeg = rad => rad * (180 / Math.PI);
 4
 5console.log("Math.sin(PI/2):", Math.sin(Math.PI / 2));          // 1
 6console.log("Math.cos(0):", Math.cos(0));                      // 1
 7console.log("Math.tan(Math.PI / 4):", Math.tan(Math.PI / 4));  // ~1
 8
 9// Using atan2 to compute angle of vector (x, y)
10const x = -1, y = 1;
11const angle = Math.atan2(y, x); // returns angle in radians taking quadrant into account
12console.log("atan2(1, -1) in degrees:", radToDeg(angle));      // 135
  • atan2 puede determinar de manera segura el ángulo de un vector, por lo que es preferible para rotaciones y cálculos de dirección.

Generación de números aleatorios (Math.random y patrones prácticos)

Math.random() devuelve un número aleatorio distribuido uniformemente en el rango [0, 1). Puede convertirlo o extenderlo según sea necesario, por ejemplo, para rangos enteros o distribuciones normales.

Los siguientes son ejemplos de funciones utilitarias de números aleatorios. Si necesita números aleatorios seguros criptográficamente, utilice crypto.getRandomValues.

 1// Random utilities using Math.random
 2
 3// Get integer in [min, max] inclusive
 4function randomInt(min, max) {
 5  // Returns integer between min and max (inclusive)
 6  return Math.floor(Math.random() * (max - min + 1)) + min;
 7}
 8console.log("randomInt(1, 6):", randomInt(1, 6)); // dice roll example
 9
10// Get float in [min, max)
11function randomFloat(min, max) {
12  return Math.random() * (max - min) + min;
13}
14console.log("randomFloat(0, 5):", randomFloat(0, 5));
15
16// Fisher-Yates shuffle
17function shuffle(array) {
18  for (let i = array.length - 1; i > 0; i--) {
19    const j = Math.floor(Math.random() * (i + 1));
20    // swap array[i] and array[j]
21    [array[i], array[j]] = [array[j], array[i]];
22  }
23  return array;
24}
25console.log("shuffle([1,2,3,4,5]):", shuffle([1,2,3,4,5]));
  • Math.random es conveniente para uso general, pero para repeticiones de juegos o criptografía, se requieren medidas para la reproducibilidad o la seguridad.

Ayudantes prácticos (clamp, lerp, mapRange, normalización de ángulos)

Es útil escribir pequeñas funciones utilitarias para tareas matemáticas de uso frecuente.

Aquí hay ejemplos de implementación de utilidades comúnmente utilizadas.

 1// Utility functions: clamp, lerp, mapRange, normalizeAngle
 2
 3// Clamp value between min and max
 4function clamp(v, min, max) {
 5  return Math.max(min, Math.min(max, v));
 6}
 7console.log("clamp(10, 0, 5):", clamp(10, 0, 5)); // 5
 8
 9// Linear interpolation: t in [0,1]
10function lerp(a, b, t) {
11  return a + (b - a) * t;
12}
13console.log("lerp(0, 10, 0.25):", lerp(0, 10, 0.25)); // 2.5
14
15// Map value from one range to another
16function mapRange(v, inMin, inMax, outMin, outMax) {
17  const t = (v - inMin) / (inMax - inMin);
18  return lerp(outMin, outMax, t);
19}
20console.log("mapRange(5, 0, 10, 0, 100):", mapRange(5, 0, 10, 0, 100));
21// -> 50
22
23// Normalize angle to [-PI, PI)
24function normalizeAngle(rad) {
25  return Math.atan2(Math.sin(rad), Math.cos(rad));
26}
27console.log("normalizeAngle(3*PI):", normalizeAngle(3 * Math.PI));
  • Este código recopila pequeñas funciones utilitarias comúnmente usadas para operaciones numéricas como limitar valores, interpolación, mapeo de rangos y normalización de ángulos. clamp limita un valor a un rango específico, mientras que lerp y mapRange realizan una interpolación suave o conversión a otro rango. Además, normalizeAngle siempre normaliza los ángulos al rango [-π, π) para estabilizar los cálculos de rotación.
  • Estos ayudantes se utilizan frecuentemente en gráficos, juegos y programación de interacciones. normalizeAngle es importante para comparar diferencias angulares y para la interpolación.

Advertencias sobre coma flotante y redondeo (precisión y comparación)

Los números en JavaScript son de coma flotante de doble precisión IEEE-754 (64 bits), por lo que se requiere precaución al comparar o redondear números. Por ejemplo, 0.1 + 0.2 !== 0.3.

Aquí hay un ejemplo de cómo manejar errores de redondeo. Es importante usar comparaciones que toleren pequeños errores o funciones de redondeo adecuadas.

1// Floating point precision example and safe equality
2console.log("0.1 + 0.2 === 0.3 :", 0.1 + 0.2 === 0.3); // false
3
4function nearlyEqual(a, b, eps = 1e-12) {
5  return Math.abs(a - b) <= eps;
6}
7console.log("nearlyEqual(0.1+0.2, 0.3):", nearlyEqual(0.1 + 0.2, 0.3));
8// -> true
  • Para comprobaciones robustas de igualdad numérica, use el error absoluto (eps) o el error relativo.

Consejos de rendimiento

Las funciones de Math están optimizadas nativamente y suelen ser más rápidas que la lógica equivalente escrita a mano. Si el mismo cálculo (por ejemplo, Math.PI / 180) se repite en un bucle, asignarlo previamente a una variable reduce la sobrecarga innecesaria.

A continuación hay un ejemplo de cómo convertir grados a radianes creando una constante fuera de un bucle.

1// Cache conversion factor for performance
2const DEG_TO_RAD = Math.PI / 180;
3const RAD_TO_DEG = 180 / Math.PI;
4
5for (let deg = 0; deg < 360; deg += 10) {
6  // Use cached constant instead of computing each time
7  const rad = deg * DEG_TO_RAD;
8  // ... do work with rad
9}
  • Es mejor verificar los puntos críticos reales con un 'profiler' antes de realizar optimizaciones.
  • Es eficiente almacenar en caché las constantes que se utilizan con frecuencia.

Compatibilidad y polyfills

Math existe desde hace mucho tiempo, pero algunas funciones como Math.cbrt, Math.log10, Math.log2 y Math.hypot pueden no estar disponibles en entornos antiguos. Prepárese un polyfill sencillo si es necesario.

A continuación se muestra un ejemplo de un polyfill sencillo para Math.log2.

1// Polyfill for Math.log2 if needed
2if (!Math.log2) {
3  Math.log2 = function(x) {
4    return Math.log(x) / Math.LN2;
5  };
6}
7console.log("Math.log2(8):", Math.log2(8));

Muchos entornos ya los soportan, pero verifique la compatibilidad si es necesario.

Verifique la compatibilidad de los navegadores según el entorno objetivo de su proyecto.

Ejemplo práctico: Actualizaciones por pasos de tiempo en una simulación física simple

Por último, aquí hay un ejemplo práctico combinando varias funciones de Math. Este es un ejemplo muy simple para actualizar la posición, la velocidad y el control de ángulos.

Es un modelo muy simplificado de un motor de física.

 1// Clamp value between min and max
 2function clamp(v, min, max) {
 3  return Math.max(min, Math.min(max, v));
 4}
 5
 6// Normalize angle to [-PI, PI)
 7function normalizeAngle(rad) {
 8  return Math.atan2(Math.sin(rad), Math.cos(rad));
 9}
10
11// Simple physics step example using Math utilities
12function step(state, dt) {
13  // state: { x, y, vx, vy, angle, angularVelocity }
14  // dt: time delta in seconds
15  // Integrate linear motion
16  state.x += state.vx * dt;
17  state.y += state.vy * dt;
18
19  // Integrate angular motion and normalize angle
20  state.angle = normalizeAngle(state.angle + state.angularVelocity * dt);
21
22  // Keep position within bounds [0, width] x [0, height]
23  state.x = clamp(state.x, 0, state.width);
24  state.y = clamp(state.y, 0, state.height);
25  return state;
26}
27
28const state = { x: 10, y: 10, vx: 2, vy: 0.5, angle: 0, angularVelocity: 0.1, width: 100, height: 100 };
29console.log("before:", state);
30step(state, 0.016); // simulate ~1 frame at 60fps
31console.log("after:", state);
  • Al combinar ayudantes de Math como estos, la lógica de simulación y animación puede hacerse más concisa.

Resumen

  • Math abarca todo, desde constantes y funciones básicas hasta avanzadas, sirviendo como la base para el procesamiento numérico.
  • Seleccione funciones según su propósito, como números aleatorios, funciones trigonométricas, exponenciales/logaritmos o redondeo; y cree pequeñas utilidades como clamp y lerp para el uso práctico.
  • Preste atención a la precisión de coma flotante y al estado de soporte de su entorno objetivo, y prepare tolerancia al error o polyfills si es necesario.

Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.

YouTube Video