it-swarm.com.de

Java: Doppelter Wertvergleich

Müssen wir vorsichtig sein, wenn Sie einen doppelten Wert mit Null vergleichen?

if ( someAmount <= 0){
.....
}
22
ZiG

Wenn du wirklich vorsichtig sein willst, kannst du mit so etwas testen, ob es sich in einem Epsilon von Null befindet 

double epsilon = 0.0000001;
if      ( f <= ( 0 - epsilon ) ) { .. }
else if ( f >= ( 0 + epsilon ) ) { .. }
else { /* f "equals" zero */ }

Oder Sie können Ihr Doppel einfach auf eine bestimmte Genauigkeit runden, bevor Sie darauf verzweigen. 

Für einige interessante Details zum Vergleich von Fehlern in Fließkommazahlen: hier ist ein Artikel von Bruce Dawson .

17
Crashworks

Für Gleichheit: (d. H. == oder !=) ja.

Bei den anderen Vergleichsoperatoren (<, >, <=, >=) hängt es davon ab, ob Sie sich auf die Edge-Fälle beziehen, z. ob < äquivalent zu <= ist, was ein weiterer Fall von Gleichheit ist. Wenn Sie sich nicht für die Edge-Fälle interessieren, spielt es normalerweise keine Rolle, obwohl es davon abhängt, woher Ihre Eingabezahlen kommen und wie sie verwendet werden. 

Wenn Sie erwarten, dass (3.0/10.0) <= 0.3 als true ausgewertet wird (es ist möglicherweise nicht der Fall, dass der Gleitkommafehler dazu führt, dass 3.0/10.0 eine Zahl ergibt, die etwas größer als 0,3 ist (z. B. 0,300000000001)) und Ihr Programm sich schlecht verhält, wenn es als false ausgewertet wird. Dies ist eine Kante Fall, und Sie müssen vorsichtig sein.

Gute numerische Algorithmen sollten fast nie von Gleichheit und Edge-Fällen abhängen. Wenn ich einen Algorithmus habe, der als Eingabe 'x', also eine Zahl zwischen 0 und 1, verwendet, sollte es im Allgemeinen keine Rolle spielen, ob 0 < x < 1 oder 0 <= x <= 1. Es gibt jedoch Ausnahmen: Sie müssen vorsichtig sein, wenn Sie Funktionen mit Verzweigungspunkten oder Singularitäten auswerten. 

Wenn ich eine Zwischengröße y habe und mit y >= 0 rechne, und ich sqrt(y) auswerte, muss ich sicher sein, dass Gleitkomma-Fehler nicht dazu führen, dass y eine sehr kleine negative Zahl ist, und die sqrt()-Funktion einen Fehler auslöst. (Angenommen, dies ist eine Situation, in der komplexe Zahlen nicht betroffen sind.) Wenn ich nicht sicher bin, was den numerischen Fehler betrifft, würde ich wahrscheinlich stattdessen sqrt(max(y,0)) auswerten. 

Für Ausdrücke wie 1/y oder log(y) ist es praktisch nicht wichtig, ob y gleich Null ist (in diesem Fall erhalten Sie einen Singularitätsfehler) oder y eine Zahl nahe Null ist (in diesem Fall erhalten Sie eine sehr große Zahl heraus, dessen Größe sehr empfindlich für den Wert von y ist - beide Fälle sind vom numerischen Standpunkt aus "schlecht", und ich muss neu bewerten, was ich versuche zu tun und nach welchem ​​Verhalten ich wann suche y-Werte liegen in der Nähe von Null.

9
Jason S

Abhängig von der Berechnung Ihres someAmountkönnen Sie bei Float/Doubles ein ungerades Verhalten erwarten

Grundsätzlich ist das Konvertieren numerischer Daten in ihre Binärdarstellung mit Float/Doubles fehleranfällig, da einige Zahlen nicht mit einem Mantis/Exponenten dargestellt werden können.

Für einige Details dazu lesen Sie diesen kleinen Artikel

Sie sollten erwägen, Java.lang.Math.signum oder Java.math.BigDecimal zu verwenden, insbesondere für die Berechnung von Währungen und Steuern

6
WiseTechi

Achten Sie auf das automatische Unboxing:

Double someAmount = null;
if ( someAmount <= 0){

Boom, NullPointerException.

5
Thilo

Ja, du solltest vorsichtig sein.

Vorschlag: Eine der besten Methoden wäre BigDecimal, um die Gleichheit/Nichtgleichheit auf 0 zu prüfen:

BigDecimal balance = pojo.getBalance();//get your BigDecimal obj

0 != balance.compareTo(BigDecimal.ZERO)

Erklärung: 

Die Funktion compareTo() vergleicht diese BigDecimal mit der angegebenen BigDecimal. Zwei BigDecimal-Objekte mit gleichem Wert, aber unterschiedlicher Skalierung (wie 2.0 und 2.00) werden von dieser Methode als gleich betrachtet. Diese Methode wird gegenüber einzelnen Methoden für jeden der sechs boolean Vergleichsoperatoren (<, ==, >, >=, !=, <=) bevorzugt. Der empfohlene Ausdruck für diese Vergleiche lautet: (x.compareTo(y) <op> 0), wobei einer der sechs Vergleichsoperatoren steht.

(Dank der SonarQube-Dokumentation)

Die Fließkomma-Mathematik ist ungenau, da es schwierig ist, solche Werte in einer binären Darstellung zu speichern. Noch schlimmer ist, dass Gleitkomma-Mathematik nicht assoziativ ist. Schieben Sie einen Float oder ein Double durch eine Reihe einfacher mathematischer Operationen, und die Antwort hängt von der Reihenfolge dieser Operationen ab, da in jedem Schritt eine Rundung ausgeführt wird.

Selbst einfache Gleitkommazuweisungen sind nicht einfach:

float f = 0.1; // 0.100000001490116119384765625
double d = 0.1; // 0.1000000000000000055511151231257827021181583404541015625

(Ergebnisse variieren je nach Compiler- und Compilereinstellungen);

Daher ist die Verwendung der Gleichheitsoperatoren (==) und der Ungleichheitsoperatoren (! =) Bei Float- oder Double-Werten fast immer ein Fehler. Stattdessen ist es am besten, Fließkommavergleiche ganz zu vermeiden. Wenn dies nicht möglich ist, sollten Sie die Verwendung einer der Float-Handling-Nummern von Java wie BigDecimal in Betracht ziehen, die Fließkommavergleiche ordnungsgemäß verarbeiten kann. Eine dritte Möglichkeit besteht darin, nicht nach Gleichheit zu suchen, sondern ob der Wert nahe genug ist. Das heißt Vergleichen Sie den absoluten Wert der Differenz zwischen dem gespeicherten Wert und dem erwarteten Wert mit einem akzeptablen Fehlerbereich. Beachten Sie, dass dies nicht alle Fälle abdeckt (beispielsweise NaN und Infinity).

1
VibrantVivek

Wenn Sie sich nicht für die Edge-Fälle interessieren, testen Sie einfach someAmount <= 0. Es macht die Absicht des Codes klar. Wenn es Ihnen egal ist, hängt es davon ab, wie Sie someAmount berechnen und warum Sie die Ungleichheit testen.

0
quant_dev

Überprüfen Sie, ob ein Double- oder Float-Wert 0 ist. Mit einem Fehlerschwellenwert wird erkannt, ob der Wert nahe 0 ist, aber nicht ganz 0. Ich denke, diese Methode ist die beste, die ich getroffen habe.

Wie teste ich, ob ein Double null ist? Beantwortet von @William Morrison

public boolean isZero(double value, double threshold){
  return value >= -threshold && value <= threshold;
}

Stellen Sie beispielsweise den Schwellenwert auf 0 ein.

System.out.println(isZero(0.00, 0));
System.out.println(isZero(0, 0));
System.out.println(isZero(0.00001, 0));

Die Ergebnisse als wahr, wahr und falsch aus den obigen Beispielcodes.

Habe Spaß @[email protected]

0
Luna Kong