Ist das Floating-Point-Round-Trip-Verhalten immer definiert, wenn der Gleitkommabereich größer ist?

8

Nehmen wir an, ich habe zwei arithmetische Typen, eine Ganzzahl, I , und eine Gleitkommazahl, F . Ich nehme auch an, dass std::numeric_limits<I>::max() kleiner ist als std::numeric_limits<F>::max() .

Nehmen wir an, ich habe einen positiven ganzzahligen Wert i . Da der darstellbare Bereich von F größer ist als I , sollte F(i) immer ein definiertes Verhalten sein.

Wenn ich jedoch einen Fließkommawert f habe, so dass f == F(i) , ist I(f) gut definiert? Mit anderen Worten, ist I(F(i)) immer definiertes Verhalten?

Relevanter Abschnitt aus dem C ++ 14-Standard:

  

4.9 Floating-Integral-Konvertierungen [conv.fpint]

     
  1. Ein prvalue eines Fließkommatyps kann in einen prvalue eines Integer-Typs konvertiert werden. Die Konvertierung wird abgebrochen.   das heißt, der Bruchteil wird verworfen. Das Verhalten ist nicht definiert, wenn der abgeschnittene Wert nicht definiert werden kann   im Zieltyp dargestellt. [ Hinweis: Wenn der Zieltyp bool ist, siehe 4.12. - Endnote ]
  2.   
  3. Ein prvalue eines Integer-Typs oder eines nicht gekapselten Aufzählungstyps kann in einen prvalue eines Floating-Werts konvertiert werden   Punkttyp Das Ergebnis ist möglichst genau. Wenn der konvertierte Wert im Bereich der Werte liegt, die dies können   dargestellt werden, aber der Wert kann nicht genau dargestellt werden, es ist eine implementierungsdefinierte Wahl von beiden   der nächst niedrigere oder höhere darstellbare Wert. [ Hinweis: Genauigkeitsverlust tritt auf, wenn der Integralwert nicht möglich ist   genau wie ein Wert des Floating-Typs dargestellt werden. - Endnote ] Wenn der konvertierte Wert außerhalb ist   der Bereich der Werte, die dargestellt werden können, ist das Verhalten undefiniert. Wenn der Quelltyp bool ist, der Wert    false wird in Null konvertiert und der Wert true wird in eins konvertiert.
  4.   
    
orlp 29.04.2015, 00:33
quelle

3 Antworten

-1

Nein.

Es ist möglich, dass i == std::numeric_limits<I>::max() , aber i in F nicht genau darstellbar ist.

  

Wenn der Wert, der konvertiert wird, in dem Bereich von Werten liegt, die dargestellt werden können, der Wert jedoch nicht genau dargestellt werden kann, ist dies eine implementierungsdefinierte Wahl für den nächst niedrigeren oder höheren darstellbaren Wert.

Da der nächsthöhere darstellbare Wert gewählt werden kann, ist es möglich, dass das Ergebnis F(i) nicht mehr in I passt, so dass die Rückrechnung ein undefiniertes Verhalten wäre.

    
orlp 29.04.2015, 00:52
quelle
4
  

Wenn ich jedoch einen Fließkommawert f habe, so dass f == F(i) , ist I(f) gut definiert? Mit anderen Worten, ist I(F(i)) immer definiertes Verhalten?

Nein.

Angenommen, I ist ein vorzeichenbehafteter Zweierkomplement-32-Bit-Integer-Typ, F ist ein Gleitkomma-Typ mit einfacher Genauigkeit mit 32 Bit und i ist die maximale positive ganze Zahl. Dies liegt im Bereich des Fließkommatyps, kann aber nicht exakt als Fließkommazahl dargestellt werden. Einige dieser 32 Bits werden für den Exponenten verwendet.

Stattdessen ist die Konvertierung von Ganzzahl zu Gleitkomma implementierungsabhängig, wird aber normalerweise durch Runden auf den nächsten darstellbaren Wert ausgeführt. Dieser gerundete Wert liegt um eins über dem Bereich des Integer-Typs. Die Konvertierung zurück in Ganzzahl schlägt fehl (besser gesagt, es ist undefiniertes Verhalten).

    
David Hammen 29.04.2015 00:45
quelle
-1

Nein. Unabhängig vom Standard können Sie nicht erwarten, dass diese Konvertierung im Allgemeinen die ursprüngliche Ganzzahl zurückgibt. Es ergibt keinen mathematischen Sinn. Aber wenn Sie in das, was Sie zitiert haben, lesen, zeigt der Standard eindeutig die Möglichkeit eines Präzisionsverlustes bei der Umwandlung von int in float.

Angenommen, Ihre Typen I und F verwenden die gleiche Anzahl von Bits. Alle Bits von I (außer möglicherweise einer, die das Zeichen speichert) werden verwendet, um den absoluten Wert der Zahl anzugeben. Auf der anderen Seite werden in F einige Bits verwendet, um den Exponenten zu spezifizieren, und einige werden für den Signifikanden verwendet. Der Bereich wird wegen des möglichen Exponenten größer sein. Aber der Signifikand wird weniger genau sein, weil es weniger Bits für seine Spezifikation gibt.

Genau wie ein Test habe ich

gedruckt %Vor%

Ich habe dann die erste Zahl in float und zurück konvertiert. Der maximale float hatte einen Exponenten von 38, und der max int hatte 10 Ziffern, also hat float eindeutig einen größeren Bereich. Aber nachdem ich den max int in float und zurück konvertiert habe, bin ich von 2147473647 nach -2147473648 gegangen. So scheint es, dass die Zahl um eine Einheit erhöht wurde und auf die negative Seite ging.

Ich habe nicht überprüft, wie viele Bits tatsächlich für float auf meinem System verwendet werden, aber es zeigt zumindest den Verlust der Genauigkeit, und es zeigt, dass gcc "aufgerundet".

    
Jim Vargo 29.04.2015 00:58
quelle