Saltar a contenido

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:

  1. Todos sus métodos son estáticos. No necesitas instanciarla.
  2. 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).
  3. 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.

1
2
3
4
5
6
// No se puede instanciar:
// Math m = new Math();   // ERROR: el constructor es privado

// Se usa directamente con el nombre de la clase:
double raiz = Math.sqrt(16.0);   // 4.0
int mayor = Math.max(3, 7);      // 7

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

public static final double PI = 3.141592653589793;
public static final double E  = 2.718281828459045;

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.

1
2
3
4
5
6
double radio = 5.0;
double area = Math.PI * Math.pow(radio, 2);   // 78.5398...
double perimetro = 2 * Math.PI * radio;       // 31.4159...

System.out.println("Area: " + area);
System.out.println("Perimetro: " + perimetro);

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

1
2
3
4
int    abs(int a)
long   abs(long a)
float  abs(float a)
double abs(double a)

Devuelve el valor sin signo. Existen cuatro versiones sobrecargadas, una por tipo numérico.

1
2
3
4
System.out.println(Math.abs(-5));      // 5
System.out.println(Math.abs(5));       // 5
System.out.println(Math.abs(-3.14));   // 3.14
System.out.println(Math.abs(0));       // 0

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

int    max(int a, int b)      // tambien long, float, double
int    min(int a, int b)      // tambien long, float, double

Devuelven el mayor o el menor de dos valores.

1
2
3
4
System.out.println(Math.max(3, 7));           // 7
System.out.println(Math.min(3, 7));           // 3
System.out.println(Math.max(-1, -5));         // -1
System.out.println(Math.max(3.14, 2.71));     // 3.14

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

1
2
3
int    clamp(long value, int min, int max)
long   clamp(long value, long min, long max)
double clamp(double value, double min, double max)

Novedad de Java 21. Limita un valor dentro de un rango [min, max]:

  • Si value < min, devuelve min.
  • Si value > max, devuelve max.
  • Si min <= value <= max, devuelve value.
1
2
3
4
System.out.println(Math.clamp(15, 0, 10));    // 10  (se satura por arriba)
System.out.println(Math.clamp(-3, 0, 10));    // 0   (se satura por abajo)
System.out.println(Math.clamp(5, 0, 10));     // 5   (dentro del rango)
System.out.println(Math.clamp(7.5, 0.0, 10.0)); // 7.5

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:

int valor = Math.max(min, Math.min(valor, max));

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.

  • floor baja siempre a la planta 2 (la planta entera inmediatamente por debajo).
  • ceil sube siempre a la planta 3 (la planta entera inmediatamente por encima).
  • round decide: 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.
  • rint es como round pero en empates (x.5) va al par.

Diferencias clave

  1. round devuelve entero (long o int); ceil, floor, rint devuelven double. Si necesitas un entero a partir de ceil, has de hacer un cast explícito: (int) Math.ceil(2.1).
  2. round(2.5) devuelve 3 (redondeo "hacia arriba" del 0.5). En cambio rint(2.5) devuelve 2 (al par más cercano).
  3. Con números negativos, ceil y floor pueden sorprender: floor(-2.1) es -3.0 (no -2.0), porque -3 es el entero inmediatamente por debajo de -2.1.
System.out.println(Math.round(2.4));    // 2
System.out.println(Math.round(2.5));    // 3
System.out.println(Math.round(2.6));    // 3
System.out.println(Math.round(-2.5));   // -2

System.out.println(Math.ceil(2.1));     // 3.0
System.out.println(Math.floor(2.9));    // 2.0
System.out.println(Math.ceil(-2.1));    // -2.0
System.out.println(Math.floor(-2.1));   // -3.0

System.out.println(Math.rint(2.5));     // 2.0  (al par)
System.out.println(Math.rint(3.5));     // 4.0  (al par)

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
1
2
3
4
5
6
7
System.out.println(Math.pow(2, 10));     // 1024.0
System.out.println(Math.sqrt(144));      // 12.0
System.out.println(Math.cbrt(27));       // 3.0
System.out.println(Math.cbrt(-8));       // -2.0
System.out.println(Math.exp(1));         // 2.718281828459045
System.out.println(Math.log(Math.E));    // 1.0
System.out.println(Math.log10(1000));    // 3.0

Casos límite:

  • Math.sqrt(-1) devuelve NaN (Not a Number), un valor especial de double que indica que el resultado no es un número real. No lanza excepción.
  • Math.log(0) devuelve -Infinity (infinito negativo).
  • Math.log(-1) devuelve NaN.
1
2
3
System.out.println(Math.sqrt(-1));   // NaN
System.out.println(Math.log(0));     // -Infinity
System.out.println(Math.log(-1));    // NaN

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:

1
2
3
System.out.println(Math.pow(2, 10));           // 1024.0
System.out.println((int) Math.pow(2, 10));     // 1024  (cast explícito)
// Para base 2, hay alternativas mas precisas con desplazamiento de bits (lo veras mas adelante)

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.

1
2
3
4
5
double grados = 90;
double radianes = Math.toRadians(grados);
System.out.println(Math.sin(radianes));   // 1.0
System.out.println(Math.cos(radianes));   // ~0.0 (puede ser 6.12E-17 por imprecision)
System.out.println(Math.tan(radianes));   // muy grande, tiende a infinito

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.

1
2
3
4
5
6
7
// Seno de 30 grados
double seno30 = Math.sin(Math.toRadians(30));
System.out.println(seno30);   // 0.5

// Arcotangente de 1, en grados
double atan1 = Math.atan(1);
System.out.println(Math.toDegrees(atan1));   // 45.0

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:

int max = 2_147_483_647;
System.out.println(max + 1);   // -2_147_483_648 (¡negativo, sin aviso!)

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.

1
2
3
4
5
6
7
int max = Integer.MAX_VALUE;           // 2_147_483_647

// Sin protección: desbordamiento silencioso
int inseguro = max + 1;                 // -2_147_483_648

// Con protección: excepción inmediata
int seguro = Math.addExact(max, 1);    // ArithmeticException: integer overflow

¿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.

1
2
3
4
5
6
7
// Ejemplo práctico: calcular el total de una compra con protección
int precioUnitario = 50_000;
int unidades = 100_000;
int total = Math.multiplyExact(precioUnitario, unidades);   // ArithmeticException: integer overflow

// ¡Cuidado! 5_000_000_000 no cabe en un int. Solución: usar la sobrecarga long
long totalLargo = Math.multiplyExact((long) precioUnitario, unidades);   // 5_000_000_000L

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

public static double random()   // devuelve un double en [0.0, 1.0)

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.

double r = Math.random();
System.out.println(r);   // por ejemplo 0.4283...

Para generar números en un rango distinto, se combina con operaciones aritméticas:

1
2
3
4
5
6
7
// Entero entre 1 y 6 (dados)
int dado = (int)(Math.random() * 6) + 1;
System.out.println(dado);

// Entero entre 10 y 50
int n = (int)(Math.random() * 41) + 10;
System.out.println(n);

Cómo funciona la fórmula:

  1. Math.random() produce [0.0, 1.0).
  2. Multiplicar por 6 produce [0.0, 6.0).
  3. Castear a int produce {0, 1, 2, 3, 4, 5} (trunca hacia abajo).
  4. 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

Use mouse to pan and zoom
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.

package dam.programacion.ud2;

/**
 * Ejemplo integrador de la Seccion 4 de UD2.
 * Demuestra el uso de:
 * - Constantes: Math.PI
 * - Funciones basicas: abs, max, min, clamp
 * - Redondeo: round, ceil, floor
 * - Potencia y raiz: pow, sqrt
 * - Trigonometria: toRadians, sin, cos
 * - Aleatorios: Math.random()
 */
public class CalculadoraLogistica {

    public static void main(String[] args) {

        // 1. Constantes: area de la base de un almacen circular
        double radio = 8.5;
        double area = Math.PI * Math.pow(radio, 2);
        System.out.println("Area del almacen circular: " + area);

        // 2. Funciones basicas: comparar dos paquetes
        int pesoPaquete1 = 25;
        int pesoPaquete2 = 18;
        int masPesado = Math.max(pesoPaquete1, pesoPaquete2);
        int masLigero = Math.min(pesoPaquete1, pesoPaquete2);
        System.out.println("Paquete mas pesado: " + masPesado + " kg");
        System.out.println("Paquete mas ligero: " + masLigero + " kg");

        // 3. clamp: limitar el descuento dentro del rango permitido [0, 30]
        int descuentoSolicitado = 45;
        int descuentoAplicado = Math.clamp(descuentoSolicitado, 0, 30);
        System.out.println("Descuento solicitado: " + descuentoSolicitado + "%");
        System.out.println("Descuento aplicado: " + descuentoAplicado + "%");

        // 4. abs: distancia entre dos almacenes en una recta numerica
        int almacenA = -150;
        int almacenB = 80;
        int distancia = Math.abs(almacenA - almacenB);
        System.out.println("Distancia entre almacenes: " + distancia + " km");

        // 5. Redondeo: precio por arriba (siempre se cobra el entero superior)
        double precioExacto = 17.23;
        int precioCobrado = (int) Math.ceil(precioExacto);
        System.out.println("Precio exacto: " + precioExacto + " EUR");
        System.out.println("Precio cobrado (ceil): " + precioCobrado + " EUR");

        // 6. round: estimacion de bultos redondeando al entero mas cercano
        double bultosEstimados = 8.4;
        long bultos = Math.round(bultosEstimados);
        System.out.println("Bultos estimados: " + bultosEstimados);
        System.out.println("Bultos redondeados: " + bultos);

        // 7. Trigonometria: proyeccion de un viaje en diagonal
        double distanciaKm = 100.0;
        double anguloGrados = 30.0;   // 30 grados respecto al norte
        double anguloRad = Math.toRadians(anguloGrados);
        double componenteNorte = distanciaKm * Math.cos(anguloRad);
        double componenteEste = distanciaKm * Math.sin(anguloRad);
        System.out.println("Componente norte: " + componenteNorte + " km");
        System.out.println("Componente este: " + componenteEste + " km");

        // 8. sqrt: teorema de Pitagoras para verificar
        double distanciaCalculada = Math.sqrt(
            Math.pow(componenteNorte, 2) + Math.pow(componenteEste, 2)
        );
        System.out.println("Distancia verificada por Pitagoras: " + distanciaCalculada + " km");

        // 9. Math.random: numero de seguimiento aleatorio en [1000, 9999]
        int seguimiento = (int)(Math.random() * 9000) + 1000;
        System.out.println("Numero de seguimiento asignado: " + seguimiento);
    }
}

Salida (los números aleatorios varían):

Area del almacen circular: 226.98006922215846
Paquete mas pesado: 25 kg
Paquete mas ligero: 18 kg
Descuento solicitado: 45%
Descuento aplicado: 30%
Distancia entre almacenes: 230 km
Precio exacto: 17.23 EUR
Precio cobrado (ceil): 18 EUR
Bultos estimados: 8.4
Bultos redondeados: 8
Componente norte: 86.60254037844386 km
Componente este: 49.99999999999999 km
Distancia verificada por Pitagoras: 99.99999999999999 km
Numero de seguimiento asignado: 4827

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

  1. ¿Por qué no es necesario escribir import java.lang.Math; al principio de tus archivos?

  2. ¿Por qué new Math() no compila? ¿Qué concepto subyace (que se verá en UD4)?

  3. Escribe una línea que calcule el área de un círculo de radio r usando Math.PI.

  4. ¿Qué devuelve Math.clamp(15, 0, 10)? ¿Y Math.clamp(-3, 0, 10)? ¿Y Math.clamp(5, 0, 10)?

  5. ¿Cuál es la diferencia entre Math.round(2.5) y Math.rint(2.5)?

  6. ¿Qué devuelven Math.ceil(2.1), Math.floor(2.9) y Math.round(2.5) respectivamente?

  7. ¿Por qué Math.floor(-2.1) devuelve -3.0 y no -2.0?

  8. ¿Qué devuelve Math.sqrt(-1)? ¿Y Math.log(0)? ¿Y Math.log(-1)?

  9. Si quieres calcular el seno de 30 grados, ¿qué expresión usarías? ¿Por qué no basta con Math.sin(30)?

  10. Escribe una línea de código que devuelva un número entero aleatorio entre 1 y 100 (ambos inclusive) usando Math.random().

  11. ¿Qué tipo de dato devuelve Math.round(double)? ¿Y Math.ceil(double)?

  12. ¿Qué problema tiene la siguiente línea y cómo se soluciona?

    int dado = Math.random() * 6 + 1;
    

  13. ¿Cómo comprobarías si un valor double x es NaN? (Pista: hay un método estático en Double.)

  14. ¿Qué son los bordillos en la analogía de clamp? ¿Qué ocurriría sin clamp?

  15. Di si cada uno de los siguientes métodos es estático o de instancia: Math.sqrt, Math.PI, Math.max, Math.random.

  16. ¿Qué diferencia hay entre max + 1 y Math.addExact(max, 1) cuando max es Integer.MAX_VALUE?

  17. ¿Por qué Math.negateExact(Integer.MIN_VALUE) lanza ArithmeticException?

Soluciones

  1. Porque Math está en el paquete java.lang, que se importa automáticamente en todos los archivos Java.

  2. No compila porque el constructor de Math es privado. El concepto subyacente es el modificador de acceso private, que se verá en UD4; por ahora basta con saber que Math es una clase de utilidades que no está pensada para instanciarse.

  3. double area = Math.PI * Math.pow(r, 2); o también double area = Math.PI * r * r;.

  4. 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).

  5. Math.round(2.5) devuelve 3 (redondea el 0.5 hacia arriba). Math.rint(2.5) devuelve 2.0 (redondea al par más cercano).

  6. Math.ceil(2.1) → 3.0, Math.floor(2.9) → 2.0, Math.round(2.5) → 3.

  7. Porque floor devuelve el entero inmediatamente por debajo. En la recta numérica, -3 está por debajo de -2.1, mientras que -2 está por encima. floor no es "quitar los decimales", es "bajar al entero inferior".

  8. Math.sqrt(-1) devuelve NaN. Math.log(0) devuelve -Infinity. Math.log(-1) devuelve NaN.

  9. Math.sin(Math.toRadians(30)). No basta con Math.sin(30) porque las funciones trigonométricas de Math trabajan en radianes, y 30 radianes no es lo mismo que 30 grados.

  10. int n = (int)(Math.random() * 100) + 1;

  11. Math.round(double) devuelve long. Math.ceil(double) devuelve double.

  12. No compila porque Math.random() devuelve double, multiplicar por 6 y sumar 1 sigue dando double, y no se puede asignar un double a un int sin 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).

  13. Double.isNaN(x). No funciona x == NaN porque NaN no es igual a sí mismo.

  14. Los bordilles son los límites min y max. Sin clamp, tendrías que escribir manualmente Math.max(min, Math.min(valor, max)), que es menos legible y más propenso a errores.

  15. Todos son estáticos (pertenecen a la clase, no a objetos). Math.PI es una constante static final; los demás son métodos static.

  16. max + 1 produce un desbordamiento silencioso y devuelve -2_147_483_648 (el número negativo más bajo). Math.addExact(max, 1) lanza ArithmeticException con el mensaje "integer overflow", permitiéndote detectar y manejar el error.

  17. Porque -Integer.MIN_VALUE sería 2_147_483_648, un valor que excede Integer.MAX_VALUE (2_147_483_647) y por tanto no cabe en un int. Es el mismo problema que Math.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

Próximos pasos:

  1. Practica en IntelliJ. Copia el ejemplo integrador, ejecútalo y modifícalo: cambia el ángulo del viaje, prueba con floor en vez de ceil para el precio, observa las imprecisiones de coma flotante.
  2. 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).
  3. Avanza a la Sección 5 de UD2. La clase Random y ThreadLocalRandom: creación con y sin semilla, métodos nextInt, nextDouble, nextBoolean, y por qué ThreadLocalRandom es la opción recomendada en código moderno. Cierra el bloque de "generación de aleatorios" que hemos iniciado aquí con Math.random().