Gibt es einen Genauigkeitsgewinn beim Werfen auf Double und Back bei der Float-Division?

Lesezeit: 7 Minuten

Benutzer-Avatar
Piotr Lopusiewicz

Was ist der Unterschied zwischen zwei folgenden?

float f1 = some_number;
float f2 = some_near_zero_number;
float result;

result = f1 / f2;

und:

float f1 = some_number;
float f2 = some_near_zero_number;
float result;

result = (double)f1 / (double)f2;

Ich interessiere mich besonders für sehr kleine f2-Werte, die beim Betrieb mit Floats +unendlich erzeugen können. Gibt es eine Genauigkeit zu gewinnen?

Einige praktische Richtlinien für die Verwendung dieser Art von Besetzung wären auch schön.

  • Wenn Sie sich Sorgen über Rundungsfehler machen, warum sollten Sie dann Float überhaupt verwenden?

    – gnasher729

    5. Februar 2015 um 20:27 Uhr

  • weil ich riesige Strukturen im RAM behalte (mehrere GB oder mehr) und die Verwendung von Doubles keine Option für die Speicherung ist; Casting hin und her ist jedoch eine Option, wenn Sie Berechnungen durchführen;

    – Piotr Lopusiewicz

    5. Februar 2015 um 22:32 Uhr

  • Bemerkenswerte Tatsache: x86 verwendet 80 Bit für die Gleitkommadivision, ob die Typen 32-Bit oder 64-Bit sind.

    – BlueRaja – Danny Pflughoeft

    5. Februar 2015 um 23:08 Uhr

Benutzer-Avatar
Patricia Shanahan

Ich gehe von IEEE 754 binärer Gleitkommaarithmetik aus, mit float 32bit und double 64-Bit.

Im Allgemeinen hat es keinen Vorteil, die Berechnung in durchzuführen doubleund in einigen Fällen kann es die Dinge durch zwei Rundungsschritte verschlimmern.

Umstellung von float zu double ist genau. Für die Eingänge Unendlich, NaN oder Nullteiler macht es keinen Unterschied. Bei einem endlichen Zahlenergebnis verlangt der IEEE 754-Standard, dass das Ergebnis das Ergebnis der reellen Zahlendivision ist f1/f2gerundet auf den Typ, der in der Division verwendet wird.

Wenn es als gemacht wird float Division, die am nächsten ist float zum exakten Ergebnis. Wenn es so gemacht wird double Teilung, es wird am nächsten sein double mit einem zusätzlichen Rundungsschritt für die Zuordnung zu result.

Für die meisten Eingaben geben die beiden die gleiche Antwort. Jeder Überlauf oder Unterlauf, der in der Division nicht aufgetreten ist, weil sie abgeschlossen wurde double wird stattdessen bei der Konvertierung passieren.

Für eine einfache Konvertierung, wenn die Antwort sehr nahe an der Mitte zwischen zwei liegt float Werte können die beiden Rundungsschritte falsch ausgewählt werden float. Ich hatte angenommen, dass dies auch für Divisionsergebnisse gelten könnte. Pascal Cuoq hat jedoch in einem Kommentar zu dieser Antwort auf ein sehr interessantes Papier aufmerksam gemacht, Harmloses doppeltes Runden von Grundrechenarten von Pierre Roux, der den Beweis fordert, dass das doppelte Runden für mehrere Operationen, einschließlich der Division, unter Bedingungen harmlos ist, die durch die Annahmen impliziert werden, die ich zu Beginn dieser Antwort gemacht habe.

  • Beachten Sie, dass / ist eine der Operationen, die nicht unter doppelter Rundung leidet, wenn die Signifikante des Zwischenformats mindestens doppelt so breit ist wie die Signifikante des Endformats. Dies ist der Fall, wenn das Zwischenformat binär64 und das Endformat binär32 ist. Figueroa hat dies für normale Zwischenergebnisse bewiesen und Pierre Roux scheint sich entschieden zu haben, es formell und für alle Fälle zu verifizieren: hal.archives-ouvertes.fr/hal-01091186/document

    – Pascal Cuoq

    5. Februar 2015 um 13:51 Uhr

  • @PascalCuoq Danke für die Informationen, die ich in die Antwort geklappt habe.

    – Patricia Shanahan

    5. Februar 2015 um 14:49 Uhr

Wenn das Ergebnis einer einzelnen Gleitkommaaddition, -subtraktion, -multiplikation oder -division sofort in a gespeichert wird floatwird es keine Verbesserung der Genauigkeit geben double für Zwischenwerte. In Fällen, in denen Operationen miteinander verkettet sind, wird die Genauigkeit jedoch häufig verbessert, indem ein Zwischentyp mit höherer Genauigkeit verwendet wird. vorausgesetzt, man verwendet sie konsequent. In Turbo Pascal circa 1986 Code wie:

Function TriangleArea(A: Single, B:Single, C:Single): Single
Begin
  Var S: Extended;  (* S stands for Semi-perimeter *)
  S := (A+B+C) * 0.5;
  TriangleArea := Sqrt((S-A)*(S-B)*(S-C)*S)
End;

würde alle Operanden von Gleitkommaoperationen auf den Typ Extended (80-Bit-Float) erweitern und sie dann beim Speichern in Variablen dieser Typen wieder in einfache oder doppelte Genauigkeit konvertieren. Sehr schöne Semantik für die numerische Verarbeitung. Turbo C dieses Bereichs verhielt sich ähnlich, versäumte es jedoch, einen numerischen Typ bereitzustellen, der in der Lage war, Zwischenergebnisse zu speichern. Das Versagen von Sprachen, einen Variablentyp bereitzustellen, der Zwischenergebnisse enthalten könnte, führte dazu, dass Leute das Konzept eines Zwischenergebnistyps mit höherer Genauigkeit unfair kritisierten, obwohl das eigentliche Problem darin bestand, dass Sprachen ihn nicht richtig unterstützten.

Wie auch immer, wenn man die obige Methode in eine moderne Sprache wie C# schreiben würde:

    public static float triangleArea(float a, float b, float c)
    {
        double s = (a + b + c) * 0.5;
        return (double)(Math.Sqrt((s - a) * (s - b) * (s - c) * s));
    }

Der Code würde gut funktionieren, wenn der Compiler zufällig die Operanden der Addition auf befördert double vor der Durchführung der Berechnung, aber das ist etwas, was es tun kann oder nicht. Wenn der Compiler die Berechnung als ausführt float, Präzision kann schrecklich sein. Wenn Sie beispielsweise die obige Formel verwenden, um die Fläche eines gleichschenkligen Dreiecks mit langen Seiten von 16777215 und einer kurzen Seite von 4 zu berechnen, liefert Eifer Promotion ein korrektes Ergebnis von 3,355443E+7, während Sie die Mathematik ausführen als float ergibt je nach Reihenfolge der Operanden 5,033165E+7 [more than 50% too big] oder 16777214.0 [more than 50% too small].

Beachten Sie, dass, obwohl Code wie der obige in einigen Umgebungen perfekt funktioniert, in anderen jedoch völlig falsche Ergebnisse liefert, Compiler im Allgemeinen keine Warnung vor der Situation ausgeben.

Obwohl einzelne Operationen auf float die sofort gespeichert werden float kann genauso genau mit Typ durchgeführt werden float wie sie mit Typ sein könnten double, hilft das eifrige Heraufstufen von Operanden oft erheblich, wenn Operationen kombiniert werden. In einigen Fällen kann das Umordnen von Operationen Probleme vermeiden, die durch den Verlust der Weiterleitung verursacht werden (z. B. verwendet die obige Formel fünf Additionen, vier Multiplikationen und eine Quadratwurzel; umschreiben der Formel als:

Math.Sqrt((a+b+c)*(b-a+c)*(a-b+c)*(a-c+b))*0.25

erhöht die Anzahl der Additionen auf acht, funktioniert aber auch dann korrekt, wenn sie mit einfacher Genauigkeit ausgeführt werden.

Benutzer-Avatar
Chux – Wiedereinsetzung von Monica

“Genauigkeitsgewinn beim Casting auf Double und Back bei Float-Division?”
Das Ergebnis hängt von anderen Faktoren ab, abgesehen von nur den beiden geposteten Methoden.


C ermöglicht die Auswertung von float Operationen auf verschiedenen Ebenen je nach geschehen FLT_EVAL_METHOD. (Siehe Tabelle unten) Wenn die aktuelle Einstellung 1 oder 2 ist, liefern die beiden von OP geposteten Methoden dieselbe Antwort.

Abhängig von anderen Code- und Compiler-Optimierungsstufen ist der Quotient result kann bei nachfolgenden Berechnungen in beiden Fällen von OP mit größerer Genauigkeit verwendet werden.

Aus diesem Grund ist ein float Teilung, die überläuft oder auf 0,0 ansteigt (ein Ergebnis mit totalem Genauigkeitsverlust) aufgrund von Extremwerten float Werte, und darf bei einer Optimierung für spätere Berechnungen tatsächlich nicht über-/unterlaufen, da der Quotient fortgeschrieben wurde als double.

Den Quotienten dazu zwingen, a zu werden float Für zukünftige Berechnungen inmitten potenzieller Optimierungen wird häufig Code verwendet volatile

volatile float result = f1 / f2;

C gibt nicht die Genauigkeit mathematischer Operationen an, aber die gemeinsame Anwendung von Standards wie IEEE754 bieten die eine einzige Operation wie binär32 Die Division führt zur nächsten darstellbaren Antwort. Sollte die Teilung bei einem breiteren Format wie z double oder long doubledann die breitere Quotientenumwandlung zurück zu float erfährt einen weiteren Rundungsschritt, der in seltenen Fällen zu einer anderen Antwort als der direkten führt float/float.


FLT_EVAL_METHOD
-1 unbestimmbar;
0 werten Sie alle Operationen und Konstanten nur auf den Bereich und die Genauigkeit des Typs aus;
1 Operationen und Konstanten des Typs auswerten float und double auf die Reichweite und Präzision der double eingeben, auswerten long double Operationen und Konstanten auf den Bereich und die Genauigkeit der long double Typ;
2 Bewerten Sie alle Operationen und Konstanten auf den Bereich und die Genauigkeit der
long double Typ.

Praktische Richtlinien:
Verwenden float vs. double um bei Bedarf Platz zu sparen. (float ist meist schmaler, selten gleich, als double) Wenn Genauigkeit wichtig ist, verwenden Sie double (oder long double).

Verwenden float vs. double Geschwindigkeit zu verbessern kann oder nicht dürfen funktionieren, wie die nativen Operationen einer Plattform alle sein können double. Es kann schneller, gleich oder langsamer sein – Profil, um es herauszufinden. Ein Großteil von C wurde ursprünglich mit entworfen double da nur Ebene FP daneben durchgeführt wurde double zu/von float Konvertierungen. Später hat C Funktionen wie hinzugefügt sinf() schneller, direkter zu erleichtern float Operationen. Je moderner also der Compiler/die Plattform, desto wahrscheinlicher float wird schneller sein. Nochmals: Profil, um es herauszufinden.

1384440cookie-checkGibt es einen Genauigkeitsgewinn beim Werfen auf Double und Back bei der Float-Division?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy