2017-11-14 2 views
12

Ich versuche, Fließkommaoperationen in Java genauer zu verstehen. Wenn ich die Dokumentation richtig gelesen, gilt die folgende für jede gegebene Doppel x:Java: Math.ulp() vs. Math.nextAfter() hinzufügen/abziehen

x - Math.ulp(x) == Math.nextAfter(x, Double.NEGATIVE_INFINITY); 
x + Math.ulp(x) == Math.nextAfter(x, Double.POSITIVE_INFINITY); 

Frage: Ist dies immer der Fall oder gibt es einige Ausnahmefälle, in denen Ergebnisse werden unterscheiden?

+2

Wenn Sie betrachten, was bei ganzzahligen Zweierpotenzen passiert, wie beispielsweise 0,25, 0,5, 1,0, 2,0 usw., sollten Sie wichtige Einblicke in Gleitkommazahlen erhalten. –

Antwort

9

Dieses Programm:

public class Test { 
    public static void main(String[] args) { 
    double x = 1; 
    System.out.println(x - Math.ulp(x) == Math.nextAfter(x, Double.NEGATIVE_INFINITY)); 
    System.out.println(x + Math.ulp(x) == Math.nextAfter(x, Double.POSITIVE_INFINITY)); 
    } 
} 

Ausgänge:

false 
true 

Die Differenz zwischen aufeinanderfolgenden Doppel Änderungen bei jeder normalen ganzzahlige Potenz von zwei, einschließlich 1,0. Einer der Tests muss fehlschlagen, weil er einen konstanten Unterschied annimmt. Math.ulp(double) ist definiert, um "den positiven Abstand zwischen diesem Gleitkommawert und dem nächstgrößeren Doppelwert" zurückzugeben, so dass der Subtraktionsvorschlag falsch ist, wenn der Abstand unterschiedlich ist.

4

der unmittelbaren Fällen würde ich denken zu überprüfen sind 0, + unendlich und -unendlich und NaN:

static void check(double x) { 
    double a, b; 
    System.out.printf(
     "%9s %9s %23s %5s%n", 
     x, a = x - Math.ulp(x), b = Math.nextAfter(x, Double.NEGATIVE_INFINITY), a == b); 
    System.out.printf(
     "%9s %9s %23s %5s%n", 
     x, a = x + Math.ulp(x), b = Math.nextAfter(x, Double.POSITIVE_INFINITY), a == b); 
    System.out.println(); 
} 

public static void main(String[] args) throws java.lang.Exception { 
    check(0); 
    check(Double.POSITIVE_INFINITY); 
    check(Double.NEGATIVE_INFINITY); 
    check(Double.NaN); 
} 

Ideone demo

Ausgang:

 0.0 -4.9E-324    -4.9E-324 true 
     0.0 4.9E-324    4.9E-324 true 

Infinity  NaN 1.7976931348623157E308 false 
Infinity Infinity    Infinity true 

-Infinity -Infinity    -Infinity true 
-Infinity  NaN -1.7976931348623157E308 false 

     NaN  NaN      NaN false 
     NaN  NaN      NaN false 

Dass die Ausdrücke aren‘ t gleich in der NaN Fall ist nicht überraschend (nach der Definition von NaN); aber diese Ausdrücke gelten auch nicht für + unendlich und -infinity (siehe die letzte Spalte).

Diese Antwort soll keine erschöpfende Liste problematischer Werte liefern, sondern zeigen, dass einige problematische Werte existieren.

+0

Dass du nicht "inkrementieren" kannst + Infinity macht Sinn. Es sollte aber für "normale" Nummern funktionieren. – assylias

+3

@assylias yep, aber OP fragt "gibt es einige Ausnahmefälle, in denen die Ergebnisse abweichen werden?" –

+0

Angesichts der Tatsache, dass die angenommene Antwort zeigt, es funktioniert nicht für eine Zweierpotenz (insbesondere für 1.0), ich denke, das ist ... etwas irreführend. –