2014-11-04 3 views
11

Ich habe einige Code-Blöcke, die tun:Ist (float) (1.2345f * 6.7809) genauer als 1.2345f * 6.7809f?

float total = <some float>; 
double some_dbl = <some double>; 

total *= some_dbl; 

Dies löst eine Compiler-Warnung, die ich zum Schweigen bringen will, aber ich weiß nicht, wie solche Warnungen ausschalten - stattdessen würde ich eher explizit Typen werfen als erforderlich. Was hat mich denken lassen ... ist ein (float)(total * some_dbl) genauer als total * (float)some_dbl? Ist es compiler- oder plattformspezifisch?

Besseres Codebeispiel (unten verlinkt):

#include <iostream> 
#include <iomanip> 
#include <cmath> 
using namespace std; 

int main() { 
    double d_total = 1.2345678; 
    float f_total = (float)d_total; 
    double some_dbl = 6.7809123; 

    double actual = (d_total * some_dbl); 
    float no_cast = (float)(f_total * some_dbl); 
    float with_cast = (float)(f_total * (float)some_dbl); 

    cout << "actual:    " << setprecision(25) << actual << endl; 
    cout << "no_cast:    " << setprecision(25) << no_cast << endl; 
    cout << "with_cast:   " << setprecision(25) << with_cast << endl; 
    cout << "no_cast, nextafter: " << setprecision(25) << nextafter(no_cast, 500.0f) << endl; 

    cout << endl; 

    cout << "Diff no_cast: " << setprecision(25) << actual - no_cast << endl; 
    cout << "Diff with_cast: " << setprecision(25) << with_cast - actual << endl; 
    return 0; 
} 

Edit: Also gab ich dies einen Schuss. Mit den Beispielen, die ich versuchte, fand ich einen schnell, wo total * (float)(some_dbl) scheint mehr genau zu sein. Ich nehme an, dass das nicht immer der Fall sein wird, aber stattdessen ist das Glück des Zeichnens, oder der Compiler schneidet Double, um zu floaten, anstatt zu runden, was möglicherweise schlechtere Ergebnisse verursacht. Siehe: http://ideone.com/sRXj1z

Edit 2: Ich bestätigte mit std::nextafter dass (float)(total * some_dbl) wird der abgeschnittene Wert zurückkehrt, und die verknüpfte Code aktualisiert. Es ist ziemlich überraschend: Wenn der Compiler in diesem Fall immer doppelt verdoppelt, dann können Sie (float)some_dbl <= some_dbl sagen, was dann with_cast <= no_cast bedeutet. Dies ist jedoch nicht der Fall! with_cast ist nicht nur größer als no_cast, sondern auch näher am tatsächlichen Wert, was irgendwie überraschend ist, da wir Informationen vor der Multiplikation verwerfen.

+0

'(float) (insgesamt * some_dbl)' sollte genauer sein, denn, nun, Mathe ... und das gilt für jede Sprache. –

+0

gcc hat eine Flagge '-ffast-math', in der es nicht mehr durch einige der Beschränkungen gebunden ist, die der Standard auf Gleitkomma setzt; Es wäre interessant zu sehen, ob das Auswirkungen auf Ihr Ergebnis hat. –

+0

Eine andere "seltsame" Sache, die Sie finden können, ist, dass die Verwendung von '1.2345678f' in Ihrer Quelle ein anderes Ergebnis ergibt als (doppelt) 1.2345678' - die Rundung zur Laufzeit kann anders auftreten zur Rundung zur Kompilierzeit. –

Antwort

2

Wenn Sie eine Operation ausführen, konvertiert der Compiler die Variablen in den größten Datentyp dieser Operation. Hier ist es doppelt. Meiner Meinung nach hat die Operation: (float) (var1f * var2) mehr Genauigkeit.

+0

Ich verstehe deine Antwort nicht, aber es ist Meinung. Mach es bitte einen Kommentar. –

+0

Ich kann keinen Kommentar abgeben. – Wolframm

+1

@NoIdeaForName Dies ist keine Meinung - das ist Tatsache. Sehen Sie sich meinen Kommentar unter dem Beitrag von OP an und untersuchen Sie die üblichen arithmetischen Konvertierungen des C-Standards im Abschnitt _6.3.1.8 Übliche arithmetische Konvertierungen_. –

1

Ich habe es getestet und sie sind nicht gleich. Das Ergebnis des Folgenden ist true. http://codepad.org/3GytxbFK

#include <iostream> 

using namespace std; 

int main(){ 
    double a = 1.0/7; 
    float b = 6.0f; 
    float c = 6.0f; 
    b = b * (float)a; 
    c = (float)((double)c * a); 
    cout << (b-c != 0.0f) << endl; 
    return 0; 
} 

Dies führt mich zu Grunde: Die Stimmen aus dem Ergebnis der Multiplikation ausgedrückt als double zu einem float wird eine bessere Chance zu abzurunden haben. Einige Bits können mit der float Multiplikation, die korrekt berücksichtigt wurde, wenn die Multiplikation an double s ausgeführt und dann an float übergeben wird, vom Ende fallen.

BTW, wählte ich 1/7 * 6, weil es im Binärformat wiederholt.

Edit: Bei Forschung, scheint es die Rundung das gleiche von Doppel für beide Umwandlung soll zu schweben und für die Vermehrung von Schwimmern, zumindest in einer Implementierung IEEE konformen 754. https://en.wikipedia.org/wiki/Floating_point#Rounding_modes

+0

Sie sollten auch 'b-c' überprüfen, bevor Sie die Multiplikationen ausführen, und sehen, welches dieser beiden Ergebnisse näher an Null ist –

+0

@MattMcNabb Dies ist, was ich mit meinem Bearbeitungsbeispiel getan habe; Tylers Logik ist, was ich auch vermutete, aber ich dachte auch, dass es mathematisch möglich wäre, dass es keinen Unterschied geben würde. Klar gibt es! – Rollie

10

Es macht ein Unterschied in Abhängigkeit von der Größe der beteiligten Zahlen, denn double ist nicht nur mehr Präzision, sondern kann auch Zahlen größer als float halten.Hier ist ein Beispiel, das eine solche Instanz zeigen:

double d = FLT_MAX * 2.0; 
float f = 1.0f/FLT_MAX; 

printf("%f\n", d * f); 
printf("%f\n", (float)d * f); 
printf("%f\n", (float)(d * f)); 

Und die Ausgabe:

2.000000 
inf 
2.000000 

Dies geschieht, weil während float offensichtlich das Ergebnis halten kann der Berechnung - 2.0, kann es nicht Halten Sie den Zwischenwert von FLT_MAX * 2.0

+0

Das macht für mich Sinn, erklärt aber nicht das oben genannte Verhalten in Bezug auf Rundungen. Warum sollte "with_cast" genauer sein als "no_cast"? (siehe Edit 2 oben) – Rollie

1

Basierend auf den Zahlen aus Ihrem Code-Dump sind zwei benachbarte mögliche Werte von float:

 d1 = 8.37149524... 
     d2 = 8.37149620... 

Das Ergebnis der Multiplikation mit doppelter Genauigkeit zu tun ist:

   8.37149598... 

, die in zwischen diesen beiden, natürlich. Konvertieren dieses Ergebnis in float ist Implementierungs-definierte, ob es "runzelt" nach oben oder unten. In Ihren Code-Ergebnissen hat die Konvertierung d1 ausgewählt, was erlaubt ist, obwohl es nicht am nächsten ist. Die Multiplikation mit gemischter Genauigkeit endete mit d2.

So können wir schließen, etwas unintelligent, dass eine Berechnung der Doppel in doppelter Genauigkeit und dann Umwandlung in float ist in einigen Fällen weniger genau als es vollständig in float Präzision!

+0

Siehe letzte Änderung; einige wirklich interessante Ergebnisse! – Rollie

Verwandte Themen