2010-09-30 20 views
16

Ich verwende ein Findbugs in einem ANT-Skript und ich kann nicht herausfinden, wie zwei meiner Fehler behoben werden. Ich habe die Dokumentation gelesen, verstehe sie aber nicht. Hier sind meine Fehler und der Code, der zu ihnen gehört:Test auf Gleitkomma-Gleichheit. (FE_FLOATING_POINT_EQUALITY)

Fehler 1: Test für Gleitpunktgleichheit. (FE_FLOATING_POINT_EQUALITY)

private boolean equals(final Quantity other) { 
    return this.mAmount == convertedAmount(other); 
} 

Fehler 2: EQ_COMPARETO_USE_OBJECT_EQUALS

public final int compareTo(final Object other) { 
    return this.description().compareTo(((Decision) other).description()); 
} 

ich die Dokumentation für die ComparesTo Ausgabe gelesen haben, die besagt,

Es wird dringend empfohlen, ist aber nicht unbedingt erforderlich, dass (x.compareTo (y) == 0) == (x.equals (y)). Im Allgemeinen sollte jede Klasse, die die Vergleichsschnittstelle implementiert und diese Bedingung verletzt, diese Tatsache deutlich angeben. Die empfohlene Sprache ist "Anmerkung: Diese Klasse hat eine natürliche Reihenfolge, die mit Gleichen nicht übereinstimmt."

und auch die Dokumentation in Bezug auf die Floating-Point-Gleichheit

Dieser Vorgang vergleicht zwei Gleitkommazahlen für die Gleichstellung. Da Fließkommaberechnungen Rundungen beinhalten können, sind die berechneten Float- und Double-Werte möglicherweise nicht genau. Verwenden Sie für Werte, die genau sein müssen, z. B. Geldwerte, einen Typ mit fester Genauigkeit, z. B. BigDecimal. Für Werte, die nicht präzise sein müssen, sollten Sie einen Vergleich für die Gleichheit in einem bestimmten Bereich in Betracht ziehen, zum Beispiel: if (Math.abs (x - y) < .0000001). Siehe die Java-Sprachspezifikation, Abschnitt 4.2.4.

Ich verstehe es aber nicht. Kann mir bitte jemand helfen?

Antwort

15

Problem 1:

Für die FE_FLOATING_POINT_EQUALITY Problem, sollten Sie nicht den Vergleich von zwei Float-Werte direkt mit dem == Operator, da aufgrund winziger Rundungsfehler, könnten die Werte semantisch sein „gleich“ für Ihre Anwendung selbst wenn die Bedingung value1 == value2 trifft nicht zu.

Um dies zu beheben, den Code wie folgt ändern:

private boolean equals(final Quantity other) { 
    return (Math.abs(this.mAmount - convertedAmount(other)) < EPSILON); 
} 

Wo EPSILON eine Konstante ist, dass Sie in Ihrem Code definieren sollen, und stellt kleine Unterschiede, die für Ihre Anwendung akzeptabel sind, zum Beispiel .0000001.

Problem 2:

Für die EQ_COMPARETO_USE_OBJECT_EQUALS Problem: Es wird dringend empfohlen, dass überall dort, wo x.compareTo(y) Null zurückgibt, x.equals(y)true sein sollte. In Ihrem Code haben Sie compareTo implementiert, aber Sie haben equals nicht überschrieben, so dass Sie die Implementierung von equals von Object erben, und die obige Bedingung nicht erfüllt ist.

Um dies zu beheben, außer Kraft setzen equals (und vielleicht hashCode) in der Klasse, so dass, wenn x.compareTo(y) 0 zurückkehrt, dann wird x.equals(y)true zurück.

+3

Ein 'equals' Methode sollte transitiv sein (http://javarevisited.blogspot.fr/2011/02/ how-to-write-equals-Methode-in-java.html) und konsistent mit der Methode 'hashCode'. Bitte erzähle uns mehr über deine'Equals'-Implementierung als '(Math.abs (this.mAmount - convertedAmount (andere)

+1

@PascalCuoq hast du das gelöst? Eine Möglichkeit, die ich mir vorstellen kann, besteht darin, Float in BigDecimal umzuwandeln, auf Genauigkeit, die Sie benötigen, und Hash dafür zu berechnen. Aber es wird Auswirkungen auf die Leistung haben. –

+1

@AlexanderMalakhov Ich bin nicht die Person, die die Frage gestellt hat. Ich habe nur einen Kommentar zur Angemessenheit der Definition einer nicht-transitiven "equals" -Methode abgegeben. Es gibt kein Problem, hier zu "lösen". Gleitkommawerte können gehashed werden und können mit den Grundoperatoren verglichen werden, die Java bereits anbietet. Jeder, der eine nicht-transitive "equals" -Methode definiert, erzeugt ein Problem für sich selbst, und die einfachste Art, das Problem zu lösen, besteht darin, es nicht erst zu erstellen. –

6

Für die Gleitkommawarnung sollten Sie bedenken, dass Schwimmer eine nicht genaue Art sind. Eine Standardreferenz, die dafür gegeben ist (die einmal vielleicht lesenswert ist) ist:

What Every Computer Scientist Should Know About Floating-Point Arithmetic von David Goldberg.

Da Floats sind keine genauen Werte - auch wenn sie aussehen das gleiche, wenn aufgerundet auf ein paar Dezimalstellen - sie können sich sehr leicht unterscheiden, und nicht übereinstimmen.

Die Comparable interface erwartet von ihrem Implementierer ein bestimmtes Verhalten; Die Warnung sagt Ihnen, dass Sie sich nicht daran halten und vorgeschlagene Maßnahmen anbieten.

1

Ich stimme nicht mit den Antworten oben überein. Equals und CompareTo sind der falsche Ort, um Epsilons in Fließkomma-Vergleichen einzuführen.

Fließkommawerte können genau mit equals verglichen werden und compareTo, nur mit dem Operator "==".
Wenn Ihre Anwendung Floats verwendet, die ein Ergebnis der Berechnung sind, müssen Sie diese Werte mit dem Epsilon-Ansatz vergleichen, dies sollte nur an der Stelle erfolgen, an der dies erforderlich ist. ZB in einer mathematischen Linienkreuzungsmethode.
Aber nicht gleich und vergleichen.

Diese Warnung ist sehr irreführend. Das bedeutet, dass der Vergleich von zwei Gleitkommazahlen, bei denen mindestens einer das Ergebnis einer Berechnung ist, zu unerwarteten Ergebnissen führen kann. jedoch oft solche Wagen, vergleichen zu können, ist nicht das Ergebnis einer Berechnung, wie

static final double INVALID_VALUE = -99.0; 
if (f == INVALID_VALUE) 

wobei f mit INVALID_VALUE initialisiert wird, in Java wird immer perfekt funktionieren. Aber Findbugs und Sonarcube werden immer noch beschweren.

So fügen Sie einfach einen Filter zu findbugs ignorieren, haben Sie zwei Klassen asuming MyPoint2D und Myrectangle2D

<Match> 
     <OR> 
      <Class name="~.*\.MyPoint2D" /> 
      <Class name="~.*\.MyRectangle2D" /> 
     </OR> 
     <Bug code="FE" /> 
     <Justification author="My Name" /> 
     <Justification 
      text="Floating point equals works (here)." /> 
    </Match> 
+0

Die Verwendung eines "ungültigen Werts" ist in erster Linie kein sauberer Ansatz. Aber die Optimierung (oft zu früh) kann zu einem solchen Hack führen und könnte sogar die beste Lösung sein. Aus Sicht des Clean-Codes sollten Sie lieber ein separates Feld verwenden, das die Information enthält, ob der Float gültig ist oder nicht. Dann müssten Sie nicht darüber nachdenken, Floats auf Gleichheit zu prüfen. – Alfe

+0

Ich stimme zu, in vielen Situationen ist es am besten, ein separates Feld zu haben, um die Gültigkeit zu zeigen. In vielen anderen Situationen nicht. (Interner Code, der für keine andere Klasse öffentlich ist) Das Hauptthema hier ist, dass der Vergleich mit floatin-Punktkonstanten funktioniert – AlexWien

+0

Die Vorstellung, dass interner Code weniger sauber sein kann, ist gefährlich. Auch interner Code benötigt möglicherweise Wartung durch einen Nachfolgeentwickler, der den Code verstehen muss. Aber ich akzeptiere in einigen Fällen das Argument "hochoptimiert". – Alfe