Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

Diskussion zu: Gleichheit von Gleitkommazahlen (inkl. etwas Theorie)

Moderationshinweis von winSharp93 (31.08.2011 - 11:59)

Diskussionen zu Gleichheit von Gleitkommazahlen (inkl. etwas Theorie)

T-Man
myCSharp.de - Member



Dabei seit:
Beiträge: 210
Herkunft: Bremen

Themenstarter:

beantworten | zitieren | melden

Sollte nicht die Prüfung des absoluten Fehlers nur dann erfolgen, wenn es sich um Zahlen verschiedenen Vorzeichens handelt?

Sonst werden besonders kleine Zahlen Grundsätzlich als ähnlich angesehen, selbst wenn sie relativ gesehen sehr unterschiedlich sind.

Hier mein Vorschlag:


        public static readonly double Epsilon = Math.Pow(2d, -52d);

        public static bool IsEqualTo(this double value, double other) { return IsEqualTo(value, other, Epsilon); }

        public static bool IsEqualTo(this double value, double other, double absEps)
        {
            var specialCase = CheckSpecialCases(value, other);
            if (specialCase.HasValue) return specialCase.Value;
            if (value ≥ .0)
            {
                if (other ≥ .0)
                    return value > other
                        ? (value - other) / value < Epsilon
                        : (other - value) / other < Epsilon;
            }
            else if (other < .0)
                return value > other
                    ? (other - value) / other < Epsilon
                    : (value - other) / value < Epsilon;
            return Math.Abs(value - other) < absEps;
        }

        private static bool? CheckSpecialCases(double value, double other)
        {
            if (value == other) return true;
            if (double.IsNaN(value)) return double.IsNaN(other);
            if (double.IsNegativeInfinity(value)) return double.IsNegativeInfinity(other);
            if (double.IsPositiveInfinity(value)) return double.IsPositiveInfinity(other);
            if (double.IsNaN(other) || double.IsInfinity(other)) return false;
            return null;
        }

Gruß
T-Man
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von T-Man am .
private Nachricht | Beiträge des Benutzers
T-Man
myCSharp.de - Member



Dabei seit:
Beiträge: 210
Herkunft: Bremen

Themenstarter:

beantworten | zitieren | melden

Bei der zweiten Variante (Vergleich der signifikanten Stellen) vermute ich ein Problem:
Wenn wir mit Zahlen mit extremen Exponenten arbeiten, können wir sie nach der Multiplikation noch immer nicht in long wandeln. Müssen wir nicht zunächst den Exponenten wegbekommen und erst danach multiplizieren?

T-Man
private Nachricht | Beiträge des Benutzers
ujr
myCSharp.de - Experte



Dabei seit:
Beiträge: 1.688

beantworten | zitieren | melden

Hallo,
Zitat von T-Man
Hier mein Vorschlag:

Bei Dir ist, z. B., -10 == 10 - das kann so nicht stimmen...
private Nachricht | Beiträge des Benutzers
T-Man
myCSharp.de - Member



Dabei seit:
Beiträge: 210
Herkunft: Bremen

Themenstarter:

beantworten | zitieren | melden

Zitat von ujr
Hallo,
Zitat von T-Man
Hier mein Vorschlag:

Bei Dir ist, z. B., -10 == 10 - das kann so nicht stimmen...

Autsch. Es muss natürlich

return Math.Abs(value - other) < absEps;

und nicht

return Math.Abs(value + other) < absEps;

sein.

Danke! Werde es korrigieren.
private Nachricht | Beiträge des Benutzers
ujr
myCSharp.de - Experte



Dabei seit:
Beiträge: 1.688

beantworten | zitieren | melden

Hallo,

in welchem Fall genau unterscheidet sich Deine Lösung von der gfoidls?

Machen Deine vielen Fallunterscheidungen nicht genau das gleiche? Math.Abs ist dann doch übersichtlicher.
private Nachricht | Beiträge des Benutzers
T-Man
myCSharp.de - Member



Dabei seit:
Beiträge: 210
Herkunft: Bremen

Themenstarter:

beantworten | zitieren | melden

gfoidl prüft immer zuerst den absoluten Fehler.

Das ist mMn nicht richtig. Diese Prüfung ist nur bei Zahlen verschieden Vorzeichens sinnvoll. gfoidl hat sie ja genau zu dem Zweck eingebaut, dass zwei Zahlen die näherungsweise 0 sind aber sich im Vorzeichen unterscheiden als gleich angesehen werden können.

Wenn man mit gfoidls Methode zwei Zahlen gleichen Vorzeichens mit einem Absolutbetrag kleiner eps_a vergleicht werden sie immer als gleich angesehen. Auch wenn der relative Unterschied sehr groß ist.

Wenn man das verhindern möchte, muss man die Vorzeichen prüfen. Und wenn man das schonmal tut, muss man nicht mehr Abs verwenden.

Die Methode muss auch nicht unbedingt 'übersichtlich' sein. Wichtiger ist doch, dass sie korrekt und möglichst performant ist.

Gruß
T-Man
private Nachricht | Beiträge des Benutzers
T-Man
myCSharp.de - Member



Dabei seit:
Beiträge: 210
Herkunft: Bremen

Themenstarter:

beantworten | zitieren | melden

Eine wichtige Frage, die man sich stellen muss ist, ob man wirklich eine sehr kleine negative und eine sehr kleine positive Zahl als gleich sehen möchte. Das hängt stark vom Kontext ab.
Wenn man dies nicht möchte, sollte man für absEps bzw. eps_a double.Epsilon setzen wenn man wenigstens -0 und 0 als gleich ansehen möchte.
Soll sogar -0 und 0 als verschieden angesehen werden, setzt man 0 für absEps ein.
private Nachricht | Beiträge des Benutzers
herbivore
myCSharp.de - Experte

Avatar #avatar-2627.gif


Dabei seit:
Beiträge: 49.486
Herkunft: Berlin

beantworten | zitieren | melden

Hallo T-Man,

man kann da geteilter Meinung sein. Nimm diesen Code:

double d1 = 0.1f;
double d2 = 0.001f;
double d3 = 1 - d1*10;
double d4 = 1 - d2*1000;
Console.WriteLine (d3);
Console.WriteLine (d4);

Die Ausgabe ist
-1,49011611938477E-08
-4,74974513053894E-08

beides kleine Zahlen mit gleichem Vorzeichen und einem ziemlichen großen relativen Fehler, gerade weil die (absoluten) Zahlen so klein sind. Ohne Rundungsfehler käme in beiden Fällen Null (0) heraus. Man würde sich also hier erhoffen, dass IsEqualsTo true liefert. Tut deine Implementierung aber gerade nicht. Der absolute Fehler kann also durchaus eine Rolle spielen, nicht nur wenn das Vorzeichen unterschiedlich ist.

herbivore

PS: Unsere Antworten haben sich überschnitten. Meine bezieht sich auf deinen Beitrag davor. Kontext ist der richtige Begriff. Es gibt hier kein pauschales Richtig oder Falsch, sondern es hängt vom Kontext ab, was gewünscht ist.
private Nachricht | Beiträge des Benutzers
T-Man
myCSharp.de - Member



Dabei seit:
Beiträge: 210
Herkunft: Bremen

Themenstarter:

beantworten | zitieren | melden

Guter Einwand. Das zeigt nochmal, dass man genau wissen muss, was man eigentlich möchte.
gfoidls Metode liefert übrigens auch nur dann true zurück, wenn man das eps_a entsprechend groß wählt.

Moderationshinweis von Abt (31.08.2011 - 13:09)

Bitte keine Full-Quotes.

private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 6.814
Herkunft: Waidring

beantworten | zitieren | melden

Hallo,

kurze Erklärung:
Das Snippet werde ich dann anpassen/ändern, wenn ein Testfall zeigt dass es ein falsches Verhalten besitzt und das nicht auf einen "Bedienfehler" zurückzuführen ist.


mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"
private Nachricht | Beiträge des Benutzers