2015-04-21 9 views
7

int Wenn ich einen Wert einer Gleitkommaberechnung auf eine Variable auf einen unsigned int mit impliziter Typumwandlung ersten zuweisen, dann zuweisen, dass, komme ich eine Antwort. Aber wenn ich die gleiche Berechnung direkt dem vorzeichenlosen int zuweiße, wiederum mit implizitem Casting, bekomme ich eine andere Antwort.durch unterschiedliches Ergebnis von „gleicher“ Art gegossene Verdutzt float

unten Beispielcode ich kompiliert und lief zu demonstrieren:

#include <iostream> 



int 
main(int argc, char** argv) 
{ 
    float payloadInTons = 6550.3; 


    // Above, payloadInTons is given a value. 
    // Below, two different ways are used to type cast that same value, 
    // but the results do not match. 
    float tempVal = payloadInTons * 10.0; 
    unsigned int right = tempVal; 
    std::cout << " right = " << right << std::endl; 


    unsigned int rawPayloadN = payloadInTons * 10.0; 
    std::cout << " wrong = " << rawPayloadN << std::endl; 


    return 0; 
} 

Hat jemand Einblick, warum „richtig“ ist richtig, und „falsch“ ist falsch?

By the way, ich bin mit gcc 4.8.2 auf Ubuntu 14.04 LTS, wenn es darauf ankommt.

+2

Es wäre ein bisschen mehr ... beeindruckend, wenn Sie auch die Ausgabe, die Sie sehen, mit einschließen. Auf [ideone.com] (http://ideone.com/0SMdPN) habe ich 'right = 65503 wrong = 65502'. – unwind

+3

Dies hat das C-Tag, aber es sieht aus wie C++. –

+2

Es ist wegen der Präzision. Wenn Sie beide mit "100.0" anstelle von "10.0" multiplizieren, werden Sie es verstehen. –

Antwort

7

Sie double Literale verwenden. Mit richtigen float Literalen ist alles in Ordnung.

int 
main(int argc, char** argv) 
{ 
    float payloadInTons = 6550.3f; 
    float tempVal = payloadInTons * 10.0f; 

    unsigned int right = tempVal; 
    std::cout << "  right = " << right << std::endl; 

    unsigned int rawPayloadN = payloadInTons * 10.0f; 
    std::cout << "also right = " << rawPayloadN << std::endl; 


    return 0; 
} 

Ausgang:

 right = 65503 
also right = 65503 
+0

Alternativ könnte man überall Doppel verwenden, denke ich. –

+0

@ SamuelEdwinWard natürlich. Solange es überall ist, werden solche Diskrepanzen nicht passieren. – Quentin

4

Nach akzeptieren Antwort

Dies ist kein double vs. float Problem. Es ist eine binäre Gleitkomma und Konvertierung in int/unsigned Problem.

Typische float verwendet binary32 Darstellung mit nicht exakte Darstellung von Werten wie 6550,3 nicht geben.

float payloadInTons = 6550.3; 
// payloadInTons has the exact value of `6550.2998046875`. 

Multiplizieren mit 10.0 unten, versichert die Berechnung mit mindestens double Präzision mit einem exakten Ergebnis 65502.998046875 erfolgt. Das Produkt wird dann zurück in float konvertiert. Der double Wert ist nicht genau darstellbar in float und so abgerundet ist, auf die besten float mit einem genauen Wert von 65503.0. Dann wandelt tempValright wie mit einem Wert von 65503 gewünscht.

float tempVal = payloadInTons * 10.0; 
unsigned int right = tempVal; 

Multiplizieren mit 10.0 unten, versichert die Berechnung mit mindestens double Präzision mit einem exakten Ergebnis 65502.998046875 nach wie vor durchgeführt wird. Dieses Mal wird der Wert direkt an unsigned rawPayloadN mit dem unerwünschten mit einem Wert von 65502 umgewandelt. Dies liegt daran, dass der Wert abgeschnitten und nicht gerundet ist.

unsigned int rawPayloadN = payloadInTons * 10.0; 

Die erste „gearbeitet“, weil der Umsatz betrug double zu float zu unsigned. Dies beinhaltet 2 Konvertierungen mit ist normalerweise schlecht. In diesem Fall machten 2 Fehler ein Recht.


Lösung

Hatte Code versucht float payloadInTons = 6550.29931640625; (die nächste kleinste float Zahl), die beide Ergebnis 65502 gewesen wäre.

Die „richtige“ Art und Weise einen Gleitkommawert zu einem gewissen Integer-Typ zu konvertieren ist oft zu Runde das Ergebnis und dann die Typumwandlung durchführen

float tempVal = payloadInTons * 10.0; 
unsigned int right = roundf(tempVal); 

. Hinweis: Dieses ganze Thema ist Komplikation durch den Wert von FLT_EVAL_METHOD. Wenn der Wert des Benutzers nicht Null ist, tritt bei höherer Genauigkeit kann Punktberechnungs floating als erwartet.

printf("FLT_EVAL_METHOD %d\n", (int) FLT_EVAL_METHOD); 
+0

Ich verstehe Ihre Erklärung und mag die deterministische Verwendung von Roundf() für zukünftige Verwendung. – donjuedo

+0

@donjuedo Der Punkt ist, dass die Verwendung von richtigen 'float' Literalen das Problem nicht lösen kann. Abhängig von 'FLT_EVAL_METHOD' können' payloadInTons * 10.0f' und 'payloadInTons * 10.0' den exakt gleichen Code ergeben. Das Hauptproblem ist, dass Werte in der Nähe von '6550.3' ein Ergebnis von' 65503' erwarten lassen. Dies wird nicht bei Verwendung von "richtigen Literalen" auftreten, sondern durch Verwendung von Runden. – chux

+0

Rechts. Für mich gibt es zwei Punkte, den einen, den ich angegeben habe (warum ist es anders), und den anderen, den ich nicht gemacht habe, aber du hast mir geholfen (wie man es zuverlässig macht). Die erste hatte mit meiner falschen Prämisse zu tun, dass die gesamte Berechnung mit float durchgeführt wurde. Ich konnte sehen, wie die Konversion zu int fehlerhaft sein könnte, aber ich vermisste, wie zwei Arten der Verwendung von float 2 Antworten erhielten. Eine tatsächlich verwendete Doppelgänger war mir nicht bewusst, daher der Unterschied. Ich stimme Ihnen zu, dass die Verwendung von "richtigen Literalen" nicht die robuste Methode ist, jedes Mal das richtige Ergebnis zu erzielen. – donjuedo

Verwandte Themen