Frage

In C89, Boden () gibt eine doppelte. Ist die folgende garantiert arbeiten?

double d = floor(3.0 + 0.5);
int x = (int) d;
assert(x == 3);

Meine Sorge ist, dass das Ergebnis des Bodens nicht genau darstellbare in IEEE sein könnte 754. So d etwas wie 2,99999 bekommt, und x endet 2 zu sein.

Für die Antwort auf diese Frage ja sein, alle ganzen Zahlen im Bereich eines int genau darstellbar sein als verdoppelt und Boden muss immer wieder zurückkehren, die genau dargestellte Wert.

War es hilfreich?

Lösung

Alle ganzen Zahlen können genaue Darstellung Gleitkomma, wenn Ihr Gleitkommatyps die erforderlichen Mantissenbits unterstützt. Da double 53 Bits für Mantissen verwendet, kann es speichert alle 32-Bit-ints genau. Immerhin konnte man nur den Wert als Mantisse mit Null Exponenten gesetzt.

Andere Tipps

Wenn das Ergebnis des Bodens () nicht genau darstellbar ist, was erwarten Sie den Wert von d zu sein? Sicher, wenn Sie haben got die Darstellung einer Gleitkommazahl in eine Variable, dann per Definition ist es genau darstellbare ist es nicht? Sie haben got die Darstellung in d ...

(Zusätzlich Mehrdad Antwort ist korrekt für die 32-Bit ints. In einem Compiler mit einem 64-Bit-Doppel und ein 64-Bit-int, haben Sie mehr Probleme natürlich bekam ...)

EDIT: Vielleicht meinte man „das theoretische Ergebnis des Bodens (), das heißt die größte ganze Zahl kleiner als oder gleich das Argument, kann nicht als int darstellbar sein“. Das ist sicher richtig. Einfache Möglichkeit, dies für ein System zeigt, in dem int 32 Bit:

int max = 0x7fffffff;
double number = max;
number += 10.0;
double f = floor(number);
int oops = (int) f;

Ich kann nicht ohne Weiteres erinnern, was C tut, wenn Konvertierungen von Gleitkommaüberlauf auf ganzzahlige ... aber es wird hier passieren wird.

EDIT: Es gibt noch andere interessante Situationen zu berücksichtigen. Hier einige C # -Code und Ergebnisse -. Ich würde zumindest vorstellen ähnlich Dinge in C. In C # passieren würde, double definiert 64 Bit zu sein, und so ist long

using System;
class Test
{
    static void Main()
    {
        FloorSameInteger(long.MaxValue/2);
        FloorSameInteger(long.MaxValue-2);
    }

    static void FloorSameInteger(long original)
    {
        double convertedToDouble = original;
        double flooredToDouble = Math.Floor(convertedToDouble);
        long flooredToLong = (long) flooredToDouble;

        Console.WriteLine("Original value: {0}", original);
        Console.WriteLine("Converted to double: {0}", convertedToDouble);
        Console.WriteLine("Floored (as double): {0}", flooredToDouble);
        Console.WriteLine("Converted back to long: {0}", flooredToLong);
        Console.WriteLine();
    }
}

Ergebnisse:

  

Original-Wert: 4611686018427387903
  Umgerechnet auf double:   4.61168601842739E + 18
Floored (als doppelt): 4.61168601842739E + 18
  Umgewandelt zurück zu lang:   4611686018427387904
  
  Originalwert: 9223372036854775805
  Umgerechnet auf double:   9.22337203685478E + 18
Floored (als doppelt): 9.22337203685478E + 18
  Umgewandelt zurück zu lang:   -9223372036854775808

Mit anderen Worten:

(long) floor((double) original)

ist nicht immer die gleiche wie original. Dies sollte nicht als eine Überraschung kommen - es gibt mehr lange Werte als verdoppelt (angesichts der NaN-Werte) und viele Doppel nicht ganze Zahlen sind, so können wir alle lange genau darstellbare nicht erwarten. Jedoch sind alle 32-Bit-Integer sind darstellbare als verdoppelt.

Ich glaube, du bist ein bisschen verwirrt über das, was Sie fragen wollen. floor(3 + 0.5) ist nicht ein sehr gutes Beispiel, weil 3, 0,5, und ihre Summe ist all genau darstellbare in jedem realen Gleitpunktformat. floor(0.1 + 0.9) wäre ein besseres Beispiel, und die eigentliche Frage ist hier nicht, ob das Ergebnis der floor genau darstellbar ist, sondern ob inexactness der Zahlen vor floor Aufruf in einem Rückgabewert führt verschieden von dem, was Sie erwarten würde, hatte alle Zahlen genau gewesen. In diesem Fall glaube ich, die Antwort ist ja, aber es hängt viel von Ihren speziellen Zahlen.

ich andere dazu einladen, diesen Ansatz zu kritisieren, wenn es schlecht ist, aber eine mögliche Abhilfe könnte sein, Ihre Anzahl von (1.0+0x1p-52) oder etwas ähnliches vor floor dem Aufruf zu multiplizieren (vielleicht nextafter verwenden wäre besser). Dies könnte für die Fälle zu kompensieren, wo ein Fehler in der letzten binären Stelle der Zahl bewirkt, dass ihm nur unten, anstatt genau auf einem ganzzahligen Wert fallen, aber es wird nicht für Fehler berücksichtigen, die über eine Anzahl von Operationen angesammelt hat. Wenn Sie das Niveau der numerischen Stabilität / Genauigkeit benötigen, müssen Sie entweder eine tiefe Analyse tun oder eine beliebige Genauigkeit verwenden oder exact-Mathematik-Bibliothek, die Ihre Zahlen korrekt verarbeiten können.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top