std::fixed komische Werte?



  • Hallo,

    ich hab ein Problem mit folgendem Code:

    #include <iostream>
    #include <limits>
    
    int main()
    {
    	float f = 1.57772181e-30f;
    
    	std::cout.precision(std::numeric_limits<float>::max_digits10);
    	std::cout << f << '\n';
    
    	std::cout.precision(50);
    	std::cout << std::fixed << f << '\n';
    }
    

    Output ist:

    1.57772181e-30
    0.00000000000000000000000000000157772181044202361082
    

    Ich hätte aber

    1.57772181e-30
    0.00000000000000000000000000000157772181000000000000
    

    erwartet. Was sollen diese Ziffern nach der maximalen Genauigkeit? Und wie kann man die wegbekommen?


  • Mod

    0.000000000000000000000000000001577721810.00000000000000000000000000000157772181 lässt sich nicht in einem float darstellen. Als normalisierte, binäre Fließkommazahl dargestellt sieht die Zahl so aus:1.1111111111111111111111111111110110010111111010001011010010010000101011100111110011100110011110001111111000101101110111101011111100010011101110001010101000010101100010111111101110011101011110001001100100\dots_{2} \cdot 2^{-100}
    Unsere Mantisse hat aber leider nur 24 Stellen. Wir runden und approximieren also
    1.111,111,111,111,111,111,111,11\dots_{2} \cdot 2^{-100} \approx 1_{2} \cdot 2^{-99} = 1.5777218104420236\dots \cdot 10^{-30}



  • Arcoth schrieb:

    0.000000000000000000000000000001577721810.00000000000000000000000000000157772181 lässt sich nicht in einem float darstellen.

    Aber ich dachte mit max_digits10 wird mir immer genau so viel angezeigt wie benötigt um die Zahl exakt zu serialisieren??

    Warum zeigt mir dann cout 1.57772181e-30 an, wenn das gar nicht als float darstellbar ist?



  • happystudent schrieb:

    Warum zeigt mir dann cout 1.57772181e-30 an, wenn das gar nicht als float darstellbar ist?

    Weil es reicht damit der Round-Trip funktioniert.
    Also damit bei float->text->float wieder am Ende der selbe float rauskommt.

    Angenommen ich hab nur 2 Bit Mantisse. Damit kann ich dann z.B. 1, 1.25, 1.5 und 1.75 darstellen.
    Dezimal brauch ich aber bloss eine Kommastelle um einen verlustfreien Round-Trip zu machen. 1 bleibt 1, 1.25 wird 1.2 (oder 1.3 - je nach Rundungsverfahren), 1.5 bleibt 1.5 und 1.75 wird 1.8. Der Round-Trip ist verlustfrei da sowohl 1.2 als auch 1.3 näher zu 1.25 als zu 1 bzw. 1.5 liegen => sie werden also beide auf 1.25 abgebildet. Und 1.8 halt auf 1.75.



  • Irgendwie versteh ichs immer noch nicht, sry...

    Ein float hat doch 7-8 signifikante Stellen. Wieso brauch ich dann überhaupt noch eine neunte Stelle für den Round-Trip? Liest der Rechner die Zahl in einer höheren Genauigkeit als float ein um sie korrekt runden zu können und wandelt sie dann in float zurück?

    Besonders wenn ich mir das Ergebnis

    0.00000000000000000000000000000157772181044202361082
    

    angucke, sind das ja viel mehr signifikante Stellen, aber diese sind ja korrekt. Wie kann das sein, dass beim serialisieren hier eine viel größere Genauigkeit zustande kommt?



  • Wo siehst du eine 9. Stelle?
    Bzw. falls dich das verwirrt: Der 1er am Anfang zählt nicht. Weil da für "normale" float Zahlen immer ein 1er ist. Ausnahme sind bloss die beiden Nullen, die beiden INFs, NaN und Denormals.

    happystudent schrieb:

    Besonders wenn ich mir das Ergebnis

    0.00000000000000000000000000000157772181044202361082
    

    angucke, sind das ja viel mehr signifikante Stellen, aber diese sind ja korrekt. Wie kann das sein, dass beim serialisieren hier eine viel größere Genauigkeit zustande kommt?

    Nein, die sind eben nicht signifikant. 😉



  • hustbaer schrieb:

    Wo siehst du eine 9. Stelle?

    Naja, std::numeric_limits<float>::max_digits10 ist ja 9, deswegen (mein Beispiel war schlecht gewählt merke ich). Also ich verstehe, dass wenn man 8 bit Genauigkeit mit richtig gerundeter letzter Stelle 9 Stellen braucht. Was ich nicht verstehe ist, wie der Compiler/PC das macht wenn er doch nur 8 Stellen hat.

    Angenommen man hat einen string mit einem float drinnen und will den korrekt runden:

    "1.12345678999999999"

    Um das überhaupt zu können müsste der Rechner das doch erstmal in einen Datentyp wandeln der mindestens 9 Stellen Genauigkeit hat, dann runden, dann zurück zu float? Also:

    "1.12345678999999999" -> 1.123456789 -> 1.12345679

    Wo ist mein Denkfehler?



  • Wenn ich 100001/300001 auf 1/3 runde habe ich in der dezimalen Darstellung trotzdem nicht weniger Nachkommastellen als vorher. Es macht daher gar keinen Sinn, die Nachkommastellen in der dezimalen Darstellung zu zaehlen.


Log in to reply