Umwandlung von Double in String zu Double-Throws Ausnahme

8

Der folgende Code löst eine Ausnahme von std :: out_of_range in Visual Studio 2013 aus, wo es meiner Meinung nach nicht sollte:

%Vor%

Ich habe den Code auch mit gcc 4.9.2 getestet und dort gibt es keine Ausnahme. Das Problem scheint durch eine ungenaue Zeichenfolgendarstellung nach der Konvertierung in Zeichenfolge verursacht worden zu sein. In Visual Studio liefert std::to_string(std::numeric_limits<double>::max())

  

179769313486231610000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000,000000

das scheint tatsächlich zu groß. In gcc ergibt es jedoch

  

179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368,000000

scheint kleiner zu sein als der übergebene Wert.

Allerdings soll nicht std::numeric_limits<double>::max() das

zurückgeben
  

maximale endliche darstellbare Fließkommazahl?

Warum steigen die String-Darstellungen aus? Was fehlt mir hier?

    
sigy 27.07.2015, 12:21
quelle

1 Antwort

1

Direkte Antwort

Gcc (und Clang und VS2105) geben korrekt den ganzzahligen Wert von (2 1024 - 1) - (2 1024-53 - 1) zurück, was dargestellt ist mit 52 Ein-Bit-Signifikanden und einem unverzerrten Exponenten von 1023 (2 1024 - 1 wäre der ganzzahlige Wert mit 1023 Ein-Bits, und ich subtrahiere nur alle Bits unterhalb der 52 des IEE754-Formats <) / p>

Ich kann bestätigen, dass eine große Integer-Bibliothek 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368L

liefert

Der vorherige exakte Fließkommawert wäre 2 971 kleiner (971 = 1023 - 52), also: 179769313486231550856124328384506240234343437157459335924404872448581845754556114388470639943126220321960804027157371570809852884964511743044087662767600909594331927728237078876188760579532563768698654064825262115771015791463983014857704008123419459386245141723703148097529108423358883457665451722744025579520L

Der nächste nicht darstellbare Wert wäre 2 971 größer, also: 179769313486231590772930519078902473361797697894230657273430081157732675805500963132708477322407536021120113879871393357658789768814416622492847430639474124377767893424865485276302219601246094119453082952085005768838150682342462881473913110540827237163350510684586298239947245938479716304835356329624224137216L

Aber der von MSVC2013 und früher verwendete Wert liegt nahe bei 2 1024 + 2 971 , das heißt: 179769313486231610731333614426100589925524828262616317947942685512308090830973387504827396012048193870699768806228404251083258210739369062217227314575410731769485876273179688476358949112102859294830297395714877595371718127781702814782017661749531126051903195165027873311156314696040132728420308633064323416064L . Da es größer ist als jeder Wert, der in IEEE754 mit doppelter Genauigkeit dargestellt werden kann, kann es nicht zu einem Doppel dekodiert werden.

Denn höchstens könnte man sagen, dass jeder Wert zwischen 2 1024 - 2 971 ( std::numeric_limits<double>::max() ) und 2 1024 sein könnte gerundet auf std::numeric_limits<double>::max() , aber Werte größer als 2 1024 sind eindeutig ein Überlauf.

Diskussion über die Genauigkeit

In einem Double sind nur 16 Dezimalziffern korrekt und alle anderen Ziffern können als Müll- oder Zufallswerte betrachtet werden, da sie nicht vom Wert selbst abhängen, sondern nur von der Art, wie Sie sie berechnen. Versuche einfach 1e + 288 (das ist schon ein großer Wert) auf maxDbl zu subtrahieren und schau was passiert:

%Vor%

Sie sollten sehen ... Unverändert.

Es sieht so aus, als ob VS 2013 ein wenig inkohärent in der Art ist, wie Gleitkommawerte gerundet werden: es hat maxDbl durch Überschreitung auf ein Bit höher als den maximal darstellbaren Wert gerundet und konnte es nicht entschlüsseln später.

Das Problem ist, dass der Standard ein %f -Format verwendet, was ein falsches Gefühl der Genauigkeit ergibt. Wenn Sie ein äquivalentes Problem in gcc sehen möchten, verwenden Sie einfach:

%Vor%

Abgerundet auf 16 Ziffern mxDbl schreibt (korrekt): 1.797693134862316e + 308, kann aber nicht mehr zurückdecodiert werden

Und dieser:

%Vor%

Anzeigen:

%Vor%

TL / DR: Was ich meine ist, dass ein großer Enoudh-Double-Wert natürlich durch eine exakte ganze Zahl repräsentiert werden kann (nach IEEE754). Aber es repräsentiert alle ganzen Zahlen zwischen der Hälfte der vorherigen und der Hälfte der nächsten. Daher kann jede Ganzzahl in diesem Bereich eine akzeptable Darstellung für das Double sein, und ein Wert gerundet bei 16 Dezimalziffern sollte akzeptabel sein, aber die aktuellen Standardbibliotheken erlauben nur, dass der maximale Gleitkommawert abgeschnitten wird bei 16 Dezimalziffern. Aber VS2013 gab eine Zahl über dem Maximum des Bereichs, was auf jeden Fall ein Fehler war.

Referenz

IEEE Gleitkomma auf Wikipedia

    
Serge Ballesta 27.07.2015 16:57
quelle