Math.nextUp în Java

Lucrează cu „pașii” dintre numerele double: valoarea imediat următoare sau anterioară și mărimea pasului (ulp).

Sintaxă
static double nextUp(double d)
static double nextDown(double d)   // Java 8
static double nextAfter(double start, double directie)
static double ulp(double d)   // Java 5

Ce face Math.nextUp

Numerele double nu sunt continue: între două valori reprezentabile există un „pas”. Math.nextUp(d) returnează cel mai mic double mai mare decât d, iar Math.nextDown(d) cel mai mare double mai mic decât d. Math.nextAfter(start, directie) face un pas de la start spre directie.

Math.ulp(d) (unit in the last place) returnează mărimea acestui pas în jurul lui d. Pasul crește odată cu numărul: lângă 1 este aproximativ 2.2 × 10-16, dar lângă 1016 este 2. Este măsura corectă a preciziei unui calcul și o toleranță bună pentru comparații.

Parametri

Parametru Descriere
d / start Valoarea de pornire.
directie Pentru nextAfter: spre ce valoare se face pasul.

Ce returnează

Un double: valoarea vecină sau mărimea pasului.

Exemple

Exemplul 1: Vecinii lui 1.0

public class Main {
    public static void main(String[] args) {
        System.out.println(Math.nextUp(1.0));
        System.out.println(Math.nextDown(1.0));
        System.out.println(Math.nextAfter(1.0, 0));
        System.out.println(Math.ulp(1.0));
    }
}

Rezultat

1.0000000000000002
0.9999999999999999
0.9999999999999999
2.220446049250313E-16

Exemplul 2: Pasul crește cu mărimea numărului

La 1016, pasul este 2: adunarea lui 1 nu mai schimbă valoarea.

public class Main {
    public static void main(String[] args) {
        double[] valori = {1, 1000, 1e16};
        for (double v : valori) {
            System.out.println("ulp(" + v + ") = " + Math.ulp(v));
        }
        double mare = 1e16;
        System.out.println(mare + 1 == mare);
    }
}

Rezultat

ulp(1.0) = 2.220446049250313E-16
ulp(1000.0) = 1.1368683772161603E-13
ulp(1.0E16) = 2.0
true

Greșeli frecvente

  • Folosești o toleranță fixă (de exemplu 1e-9) pentru numere foarte mari: acolo pasul dintre valori este mai mare decât toleranța. O toleranță relativă sau bazată pe ulp este mai corectă.

Comenzi înrudite

  • Math.getExponent: getExponent() dă exponentul binar al unui double; scalb(d, n) înmulțește rapid și exact cu 2 la puterea n.
  • Math.fma: Calculează a × b + c cu o singură rotunjire la final (fused multiply-add), mai precis decât a * b + c.
  • Double.compare: Compară două valori double și returnează negativ, 0 sau pozitiv, tratând corect NaN și -0.0.