2017-11-09 3 views
2

Warum sind die folgenden in Go ungleich? Ist das ein Fehler, oder ist es Absicht? Wenn es von Entwurf ist, warum tritt das auf und ist diese Art von Verhalten überall dokumentiert?Warum gibt es einen Unterschied zwischen Fließkomma-Multiplikation mit Literalen vs. Variablen in Go?

https://play.golang.org/p/itEV9zwV2a

package main 

import (
    "fmt" 
) 

func main() { 
    x := 10.1 

    fmt.Println("x == 10.1:  ", x == 10.1) 
    fmt.Println("x*3.0 == 10.1*3.0:", x*3.0 == 10.1*3.0) 
    fmt.Println("x*3.0:   ", x*3.0) 
    fmt.Println("10.1*3.0:   ", 10.1*3.0) 
} 

Erzeugt:

x == 10.1:   true 
x*3.0 == 10.1*3.0: false 
x*3.0:    30.299999999999997 
10.1*3.0:   30.3 

anzumerken, dass die gleiche Gleitpunktarithmetik wird, mit unterschiedlicher Syntax nur durchgeführt wird. Warum ist das Ergebnis anders? Ich würde erwarten, 10.1*3.0 gleich wie in der x*3.0 Beispiel.

+0

Mögliche Duplikate von [Ist Fließkomma-Mathematik gebrochen?] (Https://stackoverflow.com/questions/588004/is-floating-point-math-broken) –

+0

Ken, danke. Ich denke nicht, dass dies das gleiche Problem ist, da es sich bei dieser Frage um die bekannten Gleitkomma-mathematischen Genauigkeitsprobleme in vielen Sprachen handelt. Hier wird die gleiche Fließkomma-Mathematik ausgeführt, jedoch nur mit unterschiedlicher Syntax. Doch das Ergebnis ist anders. Zur Klarstellung würde ich erwarten, dass "10.1 * 3.0" gleich "30.299999 ..." ist, wie im Beispiel "x * 3.0". –

+2

Die Aussage, dass "die gleiche Fließkomma-Mathematik ausgeführt wird", ist der Punkt, an dem Sie falsch liegen. – hobbs

Antwort

5

Konstanten und Zahlenliterale in Go sind untypisiert und haben unbegrenzte Genauigkeit. In dem Moment, in dem es als spezifischer Typ gespeichert werden muss, gelten die Grenzen dieses Typs. Wenn Sie also x := 10.1 deklarieren, wird dieses Literal in eine float umgewandelt und verliert etwas an Genauigkeit. Aber wenn Sie speziell 10.1*3.0 tun, haben diese ihre volle Präzision.

Siehe den "Floats" Header in diesem Artikel.

Numerische Konstanten leben in einem beliebigen numerischen Bereich; sie sind nur reguläre Nummern. Wenn sie jedoch einer Variablen zugewiesen sind, muss der Wert in das Ziel passen. Wir können eine Konstante mit einem sehr großen Wert deklarieren:

const Huge = 1e1000 

-Das ist nur eine Zahl, schließlich aber wir können es nicht zuordnen oder auch ausdrucken. Diese Aussage wird nicht einmal kompilieren:

fmt.Println(Huge) 

Der Fehler ist "konstant 1.00000e + 1000 überläuft FLOAT64", was wahr ist. Aber Huge könnte nützlich sein: Wir können es in Ausdrücken mit anderen Konstanten verwenden und den Wert dieser Ausdrücke verwenden, wenn das Ergebnis im Bereich eines float64 dargestellt werden kann.

Wie es das tatsächlich tut, vor allem in dem gegebenen Huge Fall, weiß ich nicht.

+0

Interessant. Das würde mich erwarten lassen, dass Go eine Warnung auslöst, wenn eine bestimmte Konstante oder ein Literal nicht genau in einem 'float64' (wie 10.1 oder .1) ausgedrückt werden kann, genauso, als ob eine große Zahl einen int- oder float-Typ überläuft. I.e. 10.1 kann nicht wirklich in ein float64 oder ein float beliebiger Länge passen. Vielleicht gab es eine Design-Entscheidung, dies nicht zu tun, weil die Warnungen so üblich sein würden. –

+1

Der Compiler speichert sie als 'big.Float's (oder' big.Int's oder 'big.Rat's), nicht überraschend. Denken Sie daran, der Go-Compiler ist in Go geschrieben :) – hobbs

+0

BTW: die nächste [binary32] (https://en.wikipedia.org/wiki/Single-precision_floating-point_format) bis 10.1 ist 10.100000381 ... – chux

3

The Go Programming Language Specification

Constants

Numerische Konstanten repräsentieren genaue Werte von beliebiger Genauigkeit und tun nicht überläuft. Folglich gibt es keine Konstanten, die die IEEE-754 negativen Null-, Unendlich- und Nicht-eine-Zahl-Werte bezeichnen.

Implementierungseinschränkung: Obwohl numerische Konstanten eine beliebige Genauigkeit in der Sprache haben, kann ein Compiler sie unter Verwendung einer internen Darstellung mit begrenzter Genauigkeit implementieren.Das heißt, jede Implementierung muss:

  • ganzzahlige Konstanten Repräsentieren mit mindestens 256 Bits.

  • Repräsentieren Gleitkommakonstanten, einschließlich den Teile eines komplexen Konstante, mit einer Mantisse von wenigstens 256 Bits und ein signierten binären Exponenten von mindestens 16 Bit.

  • Geben Sie einen Fehler, wenn eine Ganzzahlkonstante nicht genau dargestellt werden kann.

  • Geben Sie einen Fehler ein, wenn eine Gleitkomma- oder komplexe Konstante aufgrund eines Überlaufs nicht dargestellt werden kann.

  • Rund um die nächste darstellbare Konstante, wenn eine Gleitkomma- oder komplexe Konstante aufgrund von Genauigkeitsgrenzen nicht dargestellt werden kann.

Numeric types

ein numerischer Typ Sätze von Ganzzahl- oder Gleitkommawerte darstellt. Die vordeklarierte architekturunabhängige Gleitkommazahlen numerischen Typen sind:

float32  the set of all IEEE-754 32-bit floating-point numbers 
float64  the set of all IEEE-754 64-bit floating-point numbers 

Konstanten verwenden Paket math/big bei der Kompilierung für beliebige Genauigkeit Arithmetik. Variablen verwenden IEEE-754, die oft von der Hardware zur Fließkomma-Arithmetik zur Verfügung gestellt wird.

+0

Es gibt die definitive Antwort ! Ich war gerade dabei, über solche Kompilierzeit Arithmetik in einem Schnitt zu meiner Antwort zu spekulieren, die ich gerade verworfen habe :) – RayfenWindspear

+0

Danke. Dies erklärt auch speziell, dass der Compiler keine Warnung oder einen Fehler ausgeben sollte, wenn er aufgrund von Grenzwerten für die Genauigkeit * keine Gleitkommakonstante * darstellen kann (aber im Falle eines Überlaufs einen Fehler auslöst). –

Verwandte Themen