UD2 · Sección 4 — La clase Math
| Dato | Valor |
|---|---|
| Unidad Didáctica | UD2 — Programación Orientada a Objetos con la API de Java |
| Resultado de Aprendizaje | RA2 — Escribe y prueba programas sencillos, reconociendo y aplicando los fundamentos de la programación orientada a objetos. |
| Sección | 4 de 10 — La clase Math |
| Evaluación | 1.ª Evaluación |
| Duración estimada | 2 horas lectivas |
| Criterios de evaluación cubiertos | CE 2.2 (escribe programas simples), CE 2.4 (utiliza métodos y propiedades), CE 2.5 (llamadas a métodos estáticos), CE 2.6 (utiliza parámetros) |
| Prerrequisitos | UD1 completa, UD2 Secciones 1, 2 y 3 |
| Paquete del código de ejemplo | dam.programacion.ud2 |
| Herramientas | JDK 25, IntelliJ IDEA Community, Maven 3.9.x |
1. Punto de partida
Has llegado a la sección más "matemática" de la unidad. Tranquilo: no vamos a derivar integrales ni a resolver ecuaciones diferenciales. La clase Math de Java es una caja de herramientas con funciones elementales que cualquier programa puede necesitar: redondear un precio, calcular una distancia, obtener un valor absoluto, limitar un número dentro de un rango.
La buena noticia es que Math es una de las clases más sencillas del API. La mala noticia (que en realidad también es buena) es que todos sus métodos son estáticos: no tienes que crear objetos, no tienes que pensar en instancias. Escribes Math.algo(...) y listo. Esto la convierte en el ejemplo perfecto para consolidar lo que aprendiste sobre métodos estáticos en la Sección 2.
Al terminar esta sección serás capaz de: usar las funciones matemáticas más comunes, entender la diferencia entre Math.random() y la clase Random (que verás en la siguiente sección), y aplicar Math.clamp() para limitar valores dentro de un rango, una novedad práctica de Java 21.
2. La clase Math: anatomía
2.1. Qué es y por qué es especial
Math es una clase del paquete java.lang (no necesitas import) que contiene constantes y métodos matemáticos. Tres características la hacen peculiar:
- Todos sus métodos son estáticos. No necesitas instanciarla.
- No se puede instanciar. Su constructor es privado (un concepto que verás en UD4; por ahora basta con saber que
new Math()no compila). - Es efectivamente una "clase de utilidades". No modela un objeto del mundo real, sino que agrupa funciones que se usan sueltas.
Analogía de la caja de herramientas. Imagina una caja de herramientas en un taller. La caja en sí no es un destornillador ni un martillo; simplemente contiene herramientas que coges y usas. Math es la caja; sqrt, max, abs son las herramientas. Coges la que necesitas, la usas y la devuelves.
2.2. Tabla de categorías
Math se organiza funcionalmente en varias categorías. Aquí tienes el mapa completo; en las siguientes secciones profundizamos en cada una.
| Categoría | Miembros principales |
|---|---|
| Constantes | PI, E |
| Funciones básicas | abs, max, min, clamp (Java 21+) |
| Redondeo | round, ceil, floor, rint |
| Potencia y raíz | pow, sqrt, cbrt, exp, log, log10 |
| Trigonometría | sin, cos, tan, asin, acos, atan, toRadians, toDegrees |
| Signo | signum, copySign |
| Aritmética segura | addExact, subtractExact, multiplyExact, incrementExact, decrementExact, negateExact |
| Otros | hypot, expm1, log1p, floorDiv, floorMod, random |
En esta sección nos centraremos en las seis primeras categorías, que son las que usarás el 95% de las veces. Las dos últimas se mencionan para que sepas que existen, pero las dejamos para consulta puntual.
3. Constantes: Math.PI y Math.E
Son constantes static final (lo verás en UD4): están asociadas a la clase, no a objetos, y su valor no cambia. Se usan sin paréntesis porque son campos, no métodos.
E es la base de los logaritmos naturales y se usa en cálculos de crecimiento exponencial, decaimiento radiactivo, interés compuesto continuo, etc.
4. Funciones básicas
4.1. abs: valor absoluto
Devuelve el valor sin signo. Existen cuatro versiones sobrecargadas, una por tipo numérico.
Trampa clásica: Math.abs(Integer.MIN_VALUE) sigue devolviendo Integer.MIN_VALUE (un número negativo), porque no existe su contrapartida positiva dentro del rango de int. Es un detalle técnico derivado de la representación binaria en complemento a dos. No suele aparecer en código cotidiano, pero es bueno saberlo.
4.2. max y min
Devuelven el mayor o el menor de dos valores.
Analogía del ascensor. Imagina que max es el operador de un ascensor que solo admite a la persona más alta de dos candidatas. min es lo opuesto: solo admite a la más baja.
4.3. clamp (Java 21+): limitar a un rango
Novedad de Java 21. Limita un valor dentro de un rango [min, max]:
- Si
value < min, devuelvemin. - Si
value > max, devuelvemax. - Si
min <= value <= max, devuelvevalue.
Casos de uso típicos:
- Limitar la posición de un personaje en un videojuego dentro de la pantalla.
- Saturar un porcentaje entre 0 y 100.
- Asegurar que una nota (0-10) no se salga del rango por errores de cálculo.
Analogía del carril. Imagina que conduces por un carril con bordillos a izquierda y derecha. Si giras demasiado a la izquierda, las ruedas tocan el bordillo izquierdo y no puedes seguir (te quedas en el mínimo). Si giras demasiado a la derecha, igual con el bordillo derecho (máximo). clamp son esos bordillos: tu coche nunca sale del carril, por mucho que gires el volante.
Antes de Java 21. Sin clamp, tenías que escribir manualmente:
Funciona, pero es menos legible. clamp expresa la intención directamente.
5. Redondeo
Java ofrece cuatro funciones de redondeo distintas, cada una con su comportamiento particular. Es importante elegir la correcta según el caso.
| Método | Qué hace | Devuelve | Ejemplo |
|---|---|---|---|
round(double) |
Redondea al entero más cercano (0,5 hacia arriba) | long |
round(2.4) → 2; round(2.5) → 3; round(2.6) → 3 |
round(float) |
Igual, para float |
int |
round(2.5f) → 3 |
ceil(double) |
Redondea hacia arriba (techo) | double |
ceil(2.1) → 3.0; ceil(-2.9) → -2.0 |
floor(double) |
Redondea hacia abajo (suelo) | double |
floor(2.9) → 2.0; floor(-2.1) → -3.0 |
rint(double) |
Redondea al entero más cercano (0,5 al par) | double |
rint(2.5) → 2.0; rint(3.5) → 4.0 |
Analogía del ascensor (otra vez). Imagina un edificio con plantas numeradas 0, 1, 2, 3, etc. Y tú estás en la planta 2.4.
floorbaja siempre a la planta 2 (la planta entera inmediatamente por debajo).ceilsube siempre a la planta 3 (la planta entera inmediatamente por encima).rounddecide: 2.4 está más cerca de 2, así que te lleva a 2. Si estuvieras en 2.5 o más, te llevaría a 3.rintes comoroundpero en empates (x.5) va al par.
Diferencias clave
rounddevuelve entero (longoint);ceil,floor,rintdevuelvendouble. Si necesitas un entero a partir deceil, has de hacer un cast explícito:(int) Math.ceil(2.1).round(2.5)devuelve 3 (redondeo "hacia arriba" del 0.5). En cambiorint(2.5)devuelve 2 (al par más cercano).- Con números negativos,
ceilyfloorpueden sorprender:floor(-2.1)es-3.0(no-2.0), porque-3es el entero inmediatamente por debajo de-2.1.
Trampa de rint. No se suele usar tanto como round. Aparece en contextos estadísticos donde quieres evitar el sesgo que introduce redondear siempre el 0.5 hacia arriba.
6. Potencia, raíz y logaritmos
| Método | Qué hace | Ejemplo |
|---|---|---|
pow(double base, double exp) |
base elevado a exp | pow(2, 10) → 1024.0 |
sqrt(double a) |
Raíz cuadrada | sqrt(16) → 4.0; sqrt(-1) → NaN |
cbrt(double a) |
Raíz cúbica | cbrt(27) → 3.0; cbrt(-8) → -2.0 |
exp(double a) |
e elevado a a | exp(1) → 2.718... |
log(double a) |
Logaritmo natural (base e) | log(Math.E) → 1.0 |
log10(double a) |
Logaritmo en base 10 | log10(1000) → 3.0 |
Casos límite:
Math.sqrt(-1)devuelveNaN(Not a Number), un valor especial dedoubleque indica que el resultado no es un número real. No lanza excepción.Math.log(0)devuelve-Infinity(infinito negativo).Math.log(-1)devuelveNaN.
NaN, Infinity y -Infinity son valores especiales del estándar IEEE 754 que usa Java para double y float. Puedes comprobarlos con Double.isNaN(x) o Double.isInfinite(x).
Trampa. Para potencias de base entera pequeña, pow puede dar resultados ligeramente imprecisos por la representación en coma flotante:
7. Trigonometría
| Método | Qué hace |
|---|---|
sin(double radianes) |
Seno |
cos(double radianes) |
Coseno |
tan(double radianes) |
Tangente |
asin(double x) |
Arcoseno (devuelve radianes) |
acos(double x) |
Arcocoseno (devuelve radianes) |
atan(double x) |
Arcotangente (devuelve radianes) |
toRadians(double grados) |
Convierte grados a radianes |
toDegrees(double radianes) |
Convierte radianes a grados |
Regla fundamental: las funciones trigonométricas de Math trabajan en radianes, no en grados. Si vienes de calcular con grados (lo habitual en clase de matemáticas), debes convertir primero con toRadians.
Analogía de la regla y el transportador. En matemáticas, los ángulos se pueden medir en grados (como un transportador de 360°) o en radianes (la longitud del arco sobre el radio, una vuelta completa = 2π). Math usa radianes por consistencia con la física y la ingeniería. toRadians y toDegrees son las funciones de conversión, como una calculadora que cambia de un sistema a otro.
Trampa. Math.cos(Math.PI / 2) debería ser 0.0, pero puede devolver 6.123233995736766E-17 (un número pequeñísimo) por las imprecisiones de la coma flotante. No es un bug: es la naturaleza de los números reales en binario. Para comparar con 0, usa un epsilon: if (Math.abs(x) < 1e-9).
8. Aritmética segura: métodos *Exact (Java 8+)
Como viste en UD1 (Sección 4 — Tipos primitivos), los enteros de Java dan la vuelta silenciosamente cuando superan su rango:
En un programa real, ese desbordamiento silencioso puede causar errores muy difíciles de rastrear: un saldo que se convierte en negativo, un índice que apunta al lugar equivocado, una cantidad de productos que de repente es negativa... Para operaciones donde el desbordamiento sería catastrófico, Java 8+ ofrece los métodos *Exact de Math. Si se produce desbordamiento, lanzan una ArithmeticException en vez de devolver un valor incorrecto:
| Método | Qué hace | Lanza ArithmeticException si… |
|---|---|---|
addExact(x, y) |
x + y |
La suma se sale del rango del tipo |
subtractExact(x, y) |
x - y |
La resta se sale del rango del tipo |
multiplyExact(x, y) |
x * y |
El producto se sale del rango del tipo |
incrementExact(x) |
x + 1 |
El resultado se sale del rango del tipo |
decrementExact(x) |
x - 1 |
El resultado se sale del rango del tipo |
negateExact(x) |
-x |
El resultado se sale del rango del tipo |
Existen sobrecargas para int y para long.
¿Cuándo usarlos? Siempre que operes con cantidades que no deberían desbordarse en condiciones normales: dinero, cantidades de stock, índices de arrays calculados, contadores acumulativos. Si el desbordamiento indica un error lógico en tu programa, usa *Exact.
¿Cuándo NO usarlos? En código de rendimiento crítico donde ya sabes por diseño que el desbordamiento no va a ocurrir (por ejemplo, un contador de píxeles en un bucle controlado). Los métodos *Exact tienen una pequeñísima sobrecarga frente a los operadores nativos.
Trampa. Math.negateExact(Integer.MIN_VALUE) también lanza ArithmeticException, porque -Integer.MIN_VALUE sería 2_147_483_648, que no cabe en un int. Es el mismo caso que Math.abs(Integer.MIN_VALUE).
9. Math.random(): introducción a los aleatorios
Devuelve un número pseudoaleatorio mayor o igual que 0.0 y menor que 1.0. El intervalo es cerrado por la izquierda y abierto por la derecha: nunca devuelve exactamente 1.0.
Para generar números en un rango distinto, se combina con operaciones aritméticas:
Cómo funciona la fórmula:
Math.random()produce[0.0, 1.0).- Multiplicar por 6 produce
[0.0, 6.0). - Castear a
intproduce{0, 1, 2, 3, 4, 5}(trunca hacia abajo). - Sumar 1 produce
{1, 2, 3, 4, 5, 6}.
Por qué es un "spoiler" de la siguiente sección. Math.random() internamente usa un objeto Random compartido (un campo estático de la clase Math). Esto la hace cómoda pero menos flexible: no puedes ponerle una semilla para repetir resultados, no puedes pedir enteros directamente, no puedes pedir booleanos. Por eso, en la Sección 5 veremos la clase Random y ThreadLocalRandom, que ofrecen muchas más opciones.
Recomendación: usa Math.random() para casos rápidos y puntuales; usa Random o ThreadLocalRandom cuando necesites más control.
10. Diagrama resumen de la clase Math
graph TD
M["Clase Math<br/>(java.lang)"]
M --> C["Constantes<br/>PI, E"]
M --> B["Basicas<br/>abs, max, min, clamp"]
M --> R["Redondeo<br/>round, ceil, floor, rint"]
M --> P["Potencia y raiz<br/>pow, sqrt, cbrt, exp, log"]
M --> T["Trigonometria<br/>sin, cos, tan, atan..."]
M --> E["Aritmetica segura<br/>addExact, multiplyExact..."]
M --> A["Aleatorios<br/>random"]
A -.internamente usa.-> R2["Random interno"]
R2 -.se profundiza en.-> S5["Seccion 5: Random"]
style M fill:#1565c0,color:#fff,stroke:#0d47a1
style C fill:#2e7d32,color:#fff,stroke:#1b5e20
style B fill:#ef6c00,color:#fff,stroke:#e65100
style R fill:#6a1b9a,color:#fff,stroke:#4a148c
style P fill:#c62828,color:#fff,stroke:#b71c1c
style T fill:#00838f,color:#fff,stroke:#006064
style E fill:#4527a0,color:#fff,stroke:#311b92
style A fill:#ad1457,color:#fff,stroke:#880e4f
11. Ejemplo integrador
Programa que combina los conceptos de la sección. Imagina que estás construyendo una pequeña calculadora para una empresa de logística que necesita estimar distancias, tiempos y precios.
Salida (los números aleatorios varían):
Observa cómo la distancia verificada por Pitágoras da 99.99999... en vez de 100.0. Esa pequeña imprecisión es el coste de trabajar con coma flotante: lo mencionamos en la sección de trigonometría y aquí lo ves en acción.
12. Errores comunes
| Error | Qué pasa | Cómo evitarlo |
|---|---|---|
| Olvidar radianes en trigonometría | Math.sin(90) da 0.893... en vez de 1.0 |
Usa Math.toRadians(grados) antes de pasar a sin/cos/tan |
Confundir round, ceil, floor |
Resultados inesperados al redondear | Recuerda: round al entero más cercano, ceil siempre arriba, floor siempre abajo |
Esperar que ceil devuelva int |
ceil(2.1) devuelve 3.0 (double), no 3 |
Haz cast explícito: (int) Math.ceil(2.1) |
Olvidar el cast al usar Math.random() |
(Math.random() * 6) + 1 devuelve double, no int |
Envuelve el cast: (int)(Math.random() * 6) + 1 |
Usar Math.abs(Integer.MIN_VALUE) |
Sigue siendo negativo | No suele ocurrir en código cotidiano, pero conviene saberlo |
Comparar cos(PI/2) con 0.0 |
Falla por imprecisiones (6.12E-17) |
Compara con un epsilon: Math.abs(x) < 1e-9 |
Olvidar que clamp es de Java 21+ |
No compila en proyectos antiguos | Verifica @since 21 en el Javadoc; en Java 25 está disponible |
Pensar que Math se puede instanciar |
new Math() no compila |
Math es una clase de utilidades; todos sus miembros son estáticos |
13. Resumen
| Categoría | Métodos clave |
|---|---|
| Constantes | PI, E |
| Básicas | abs, max, min, clamp (Java 21+) |
| Redondeo | round, ceil, floor, rint |
| Potencia y raíz | pow, sqrt, cbrt, exp, log, log10 |
| Trigonometría | sin, cos, tan, asin, acos, atan, toRadians, toDegrees |
| Aritmética segura | addExact, subtractExact, multiplyExact, incrementExact, decrementExact, negateExact |
| Aleatorios | random (spoiler de la Sección 5) |
| Casos especiales | NaN, Infinity, -Infinity (IEEE 754) |
| Particularidad | Todos los métodos son estáticos; no se puede instanciar |
14. Cuestionario de autoevaluación
Preguntas
-
¿Por qué no es necesario escribir
import java.lang.Math;al principio de tus archivos? -
¿Por qué
new Math()no compila? ¿Qué concepto subyace (que se verá en UD4)? -
Escribe una línea que calcule el área de un círculo de radio
rusandoMath.PI. -
¿Qué devuelve
Math.clamp(15, 0, 10)? ¿YMath.clamp(-3, 0, 10)? ¿YMath.clamp(5, 0, 10)? -
¿Cuál es la diferencia entre
Math.round(2.5)yMath.rint(2.5)? -
¿Qué devuelven
Math.ceil(2.1),Math.floor(2.9)yMath.round(2.5)respectivamente? -
¿Por qué
Math.floor(-2.1)devuelve-3.0y no-2.0? -
¿Qué devuelve
Math.sqrt(-1)? ¿YMath.log(0)? ¿YMath.log(-1)? -
Si quieres calcular el seno de 30 grados, ¿qué expresión usarías? ¿Por qué no basta con
Math.sin(30)? -
Escribe una línea de código que devuelva un número entero aleatorio entre 1 y 100 (ambos inclusive) usando
Math.random(). -
¿Qué tipo de dato devuelve
Math.round(double)? ¿YMath.ceil(double)? -
¿Qué problema tiene la siguiente línea y cómo se soluciona?
-
¿Cómo comprobarías si un valor
double xesNaN? (Pista: hay un método estático enDouble.) -
¿Qué son los bordillos en la analogía de
clamp? ¿Qué ocurriría sinclamp? -
Di si cada uno de los siguientes métodos es estático o de instancia:
Math.sqrt,Math.PI,Math.max,Math.random. -
¿Qué diferencia hay entre
max + 1yMath.addExact(max, 1)cuandomaxesInteger.MAX_VALUE? -
¿Por qué
Math.negateExact(Integer.MIN_VALUE)lanzaArithmeticException?
Soluciones
-
Porque
Mathestá en el paquetejava.lang, que se importa automáticamente en todos los archivos Java. -
No compila porque el constructor de
Mathes privado. El concepto subyacente es el modificador de accesoprivate, que se verá en UD4; por ahora basta con saber queMathes una clase de utilidades que no está pensada para instanciarse. -
double area = Math.PI * Math.pow(r, 2);o tambiéndouble area = Math.PI * r * r;. -
Math.clamp(15, 0, 10)→10(se satura por arriba).Math.clamp(-3, 0, 10)→0(se satura por abajo).Math.clamp(5, 0, 10)→5(dentro del rango, se devuelve tal cual). -
Math.round(2.5)devuelve3(redondea el 0.5 hacia arriba).Math.rint(2.5)devuelve2.0(redondea al par más cercano). -
Math.ceil(2.1)→3.0,Math.floor(2.9)→2.0,Math.round(2.5)→3. -
Porque
floordevuelve el entero inmediatamente por debajo. En la recta numérica,-3está por debajo de-2.1, mientras que-2está por encima.floorno es "quitar los decimales", es "bajar al entero inferior". -
Math.sqrt(-1)devuelveNaN.Math.log(0)devuelve-Infinity.Math.log(-1)devuelveNaN. -
Math.sin(Math.toRadians(30)). No basta conMath.sin(30)porque las funciones trigonométricas deMathtrabajan en radianes, y 30 radianes no es lo mismo que 30 grados. -
int n = (int)(Math.random() * 100) + 1; -
Math.round(double)devuelvelong.Math.ceil(double)devuelvedouble. -
No compila porque
Math.random()devuelvedouble, multiplicar por6y sumar1sigue dandodouble, y no se puede asignar undoublea unintsin cast explícito. Solución:int dado = (int)(Math.random() * 6) + 1;(los paréntesis son importantes para que el cast se aplique solo al producto, no al+ 1). -
Double.isNaN(x). No funcionax == NaNporqueNaNno es igual a sí mismo. -
Los bordilles son los límites
minymax. Sinclamp, tendrías que escribir manualmenteMath.max(min, Math.min(valor, max)), que es menos legible y más propenso a errores. -
Todos son estáticos (pertenecen a la clase, no a objetos).
Math.PIes una constantestatic final; los demás son métodosstatic. -
max + 1produce un desbordamiento silencioso y devuelve-2_147_483_648(el número negativo más bajo).Math.addExact(max, 1)lanzaArithmeticExceptioncon el mensaje "integer overflow", permitiéndote detectar y manejar el error. -
Porque
-Integer.MIN_VALUEsería2_147_483_648, un valor que excedeInteger.MAX_VALUE(2_147_483_647) y por tanto no cabe en unint. Es el mismo problema queMath.abs(Integer.MIN_VALUE).
Evaluación del cuestionario
| Aciertos | Nivel |
|---|---|
| 15-17 | Dominas la sección. Adelante con la siguiente. |
| 11-14 | Bien, repasa los puntos fallados. |
| 7-10 | Repasa la sección, especialmente redondeo y radianes. |
| Menos de 7 | Vuelve a leer con calma y ejecuta el ejemplo integrador paso a paso en IntelliJ. |
15. Información ampliada y próximos pasos
- Javadoc oficial de
Math(Java 25): https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/Math.html - Artículo "Math.clamp in Java 21" (Baeldung): https://www.baeldung.com/java-math-clamp
- Estándar IEEE 754 en coma flotante (Wikipedia, para curiosos): https://es.wikipedia.org/wiki/IEEE_754
- Tutorial de Oracle sobre números básicos: https://docs.oracle.com/javase/tutorial/java/data/numbers.html
Próximos pasos:
- Practica en IntelliJ. Copia el ejemplo integrador, ejecútalo y modifícalo: cambia el ángulo del viaje, prueba con
flooren vez deceilpara el precio, observa las imprecisiones de coma flotante. - Experimenta con
clamp. Es una de las novedades más útiles de Java 21+. Piensa en tres escenarios donde te habría venido bien (por ejemplo, limitar una nota entre 0 y 10, una edad entre 0 y 120, una temperatura entre -50 y 60). - Avanza a la Sección 5 de UD2. La clase
RandomyThreadLocalRandom: creación con y sin semilla, métodosnextInt,nextDouble,nextBoolean, y por quéThreadLocalRandomes la opción recomendada en código moderno. Cierra el bloque de "generación de aleatorios" que hemos iniciado aquí conMath.random().