2015-01-07 6 views
9

Betrachten Sie diese zwei Fälle:Warum haben diese beiden float64s unterschiedliche Werte?

fmt.Println(912 * 0.01) 
fmt.Println(float64(912) * 0.01) 

(Go Playground link)

die zweite druckt 9,120000000000001, die eigentlich in Ordnung ist, I understand why that is happening.

Warum aber druckt die erste Zeile 9.12, ohne die ... 01 am Ende? Verarbeitet Go die zwei untypisierten Konstanten und ersetzt sie beim Kompilieren einfach durch ein 9.12-Literal?

+0

mögliche Duplikate von [Ist Fließkomma-Mathematik gebrochen?] (Http://StackOverflow.com/Questions/588004/is-floating-point-math-broken) – asawyer

+1

@asawyer Nicht wirklich. OP fragt sich, warum diese zwei unterschiedliche Ergebnisse liefern, nicht warum eines der Ergebnisse nicht genau 9.12 ist. – fuz

Antwort

14

Gemäß spec:

Konstante Ausdrücke werden immer genau ausgewertet; Zwischenwerte und die Konstanten selbst können eine Genauigkeit erfordern, die wesentlich größer ist als von jedem vordefinierten Typ in der Sprache unterstützt.

Seit

912 * 0.01 

ist ein konstanter Ausdruck, es genau ausgewertet wird. So hat Schreiben fmt.Println(912 * 0.01) den gleichen Effekt wie Schreiben fmt.Println(9.12). Wenn Sie 912 an float64 anheften, wird der andere Operand der Gleitkomma-Multiplikation implizit auch an float64 gepinnt. Daher verhält sich der Ausdruck float64(912) * 0.01 wie float64(912) * float64(0.01). 0,01 ist in einem float64 nicht genau darstellbar, daher geht die Genauigkeit an einer anderen Stelle verloren als im Ausdruck float64(912 * 0.01), der im ersten Beispiel in dem Argument fmt.Println() auftaucht und die unterschiedlichen Ergebnisse erklärt.

6

Der Grund für den unterschiedlichen Ausgang ist, dass 912 * 0.01 im ersten Fall ist die Multiplikation von 2 nicht typisieren Konstante Werte, die von beliebiger Genauigkeit ist, und nur das Ergebnis an float64 umgewandelt, wenn der Wert auf Println() geben wird. (Siehe Constant expressions Abschnitt der Language-Spezifikation für weitere Details.)

Im zweiten Fall float64(912) * 0.01 erste 912-float64 umgewandelt wird, dann ist die nicht typisierte Konstante 0.01 auf float64 umgewandelt und diese beiden Werte haben float64 multipliziert werden, die nicht willkürlich ist Präzision, und wird kein genaues Ergebnis geben.

Hinweis:

Im ersten Fall wird das Ergebnis zu float64 umgewandelt werden, wenn an den Println() geben:

fmt.Printf("%T %v\n", 912 * 0.01, 912 * 0.01) 

Ausgang:

float64 9.12 

Test it on Go Playground

+0

"Im ersten Fall wird das Ergebnis in' float64' umgewandelt, wenn es an 'Println()' "übergeben wird - Sie meinen das Ergebnis der Multiplikation zweier untypisierter Konstanten, oder? –

+0

@ AttilaO. Ja, das Ergebnis der Multiplikation. Wenn es an die Funktion 'Println()' übergeben wird, muss es in einen typisierten Wert konvertiert werden. Auch bearbeitet, um Code hinzuzufügen, um den Typ des Ergebnisses anzuzeigen. – icza