2009-03-03 10 views
32

Ich möchte eine floor Funktion mit der SyntaxVerleiht ein int nach std :: floor das richtige Ergebnis?

int floor(double x); 

aber std::floor gibt ein double. Ist

static_cast <int> (std::floor(x)); 

garantiert, um mir die richtige Ganzzahl zu geben, oder könnte ich ein off-by-one Problem haben? Es scheint zu funktionieren, aber ich würde es gerne wissen.

Für Bonuspunkte, warum zum Teufel macht std::floor eine double in erster Linie zurückgeben?

Antwort

25

Der Bereich von Doppel ist viel größer als der Bereich von 32 oder 64 Bit Ganzzahlen, weshalb std::floor eine double zurückgibt. Casting auf int sollte in Ordnung sein, solange es innerhalb des geeigneten Bereichs ist - aber bedenken Sie, dass ein double nicht alle 64-Bit-Integer genau darstellen kann, so dass Sie auch mit Fehlern enden können, wenn Sie den Punkt überschreiten, bei dem die Genauigkeit von double ist so, dass die Differenz zwischen zwei aufeinanderfolgenden Doppel größer als 1.

+0

Also, wenn es im richtigen Bereich ist, die Besetzung ist in Ordnung Wo es (etwas, das bedeutet) sagt dies in der Spezifikation? ? –

+0

Nun, es gibt zwei Operationen hier: std :: floor, und die Besetzung. Std :: floor soll eine ganze Zahl zurückgeben, und die Besetzung wird in Form eines ganzzahligen Wertes angegeben, solange std :: floor zurückkommt ein Wert, der wirklich eine exakte ganze Zahl ist, I ca nicht sehen, wie es sinnvoll wäre, dass es scheitert. –

+0

Wie würde Stock jemals etwas zurückgeben, das keine exakte ganze Zahl ist? Für kleine Doubles (epsilon << 1) können alle ganzzahligen Werte dargestellt werden, einschließlich floor (x). Für große Doppel (epsilon >> 1) können nur ganzzahlige Werte dargestellt werden, also floor (x) == x. – MSalters

12
static_cast <int> (std::floor(x)); 

macht ziemlich genau, was Sie wollen, ja. Es gibt Ihnen die nächste Ganzzahl, gerundet auf -infinity. Zumindest solange Ihre Eingabe in dem Bereich liegt, der durch Ints dargestellt werden kann. Ich bin nicht sicher, was Sie meinen, indem Sie .5 und whatnot hinzufügen, aber es wird nicht den gleichen Effekt haben

Und std :: floor gibt ein Doppel zurück, weil das das allgemeinste ist. Manchmal möchten Sie vielleicht ein Float oder Double abrunden, aber den Typ beibehalten. Das heißt, rund 1.3f zu 1.0f, anstatt zu 1.

Das wäre schwer zu tun, wenn std :: floor ein Int. (oder zumindest hat man eine extra unnötige Besetzung, die die Dinge verlangsamt).

Wenn floor nur die Rundung selbst ausführt, ohne den Typ zu ändern, können Sie dies bei Bedarf in int umwandeln.

Ein weiterer Grund ist, dass der Bereich der Doppel viel größer ist als der von Ints. Es kann nicht möglich sein, alle Doubles auf Ints zu runden.

+1

floor() Runden in Richtung -Unendlichkeit, nicht Null: Siehe http://www.cplusplus.com/reference/clibrary/cmath/floor.html –

+0

Doh, du hast natürlich recht. Fest. – jalf

5

Wenn Sie mit verschiedenen numerischen Bedingungen umgehen und verschiedene Arten von Konvertierungen kontrolliert handhaben möchten, sollten Sie sich vielleicht die Boost.NumericConversion ansehen. Diese Bibliothek erlaubt es seltsame Fälle zu behandeln (wie Out-of-Bereich, Runden, Bereiche, etc.)

Hier ist das Beispiel aus der Dokumentation:

#include <cassert> 
#include <boost/numeric/conversion/converter.hpp> 

int main() { 

    typedef boost::numeric::converter<int,double> Double2Int ; 

    int x = Double2Int::convert(2.0); 
    assert (x == 2); 

    int y = Double2Int()(3.14); // As a function object. 
    assert (y == 3) ; // The default rounding is trunc. 

    try 
    { 
     double m = boost::numeric::bounds<double>::highest(); 
     int z = Double2Int::convert(m); // By default throws positive_overflow() 
    } 
    catch (boost::numeric::positive_overflow const&) 
    { 
    } 

    return 0; 
} 
2

Die meisten der Standard-Mathematik-Bibliothek verwendet verdoppelt aber bietet auch Float-Versionen. std :: floorf() ist die Single-Precision-Version von std :: floor(), wenn Sie Doubles nicht verwenden möchten.

Edit: Ich habe einen Teil meiner vorherigen Antwort entfernt. Ich hatte festgestellt, dass der Floor beim Casting nach int überflüssig war, aber ich habe vergessen, dass dies nur für positive Gleitkommazahlen gilt.

6

die C++ Standard sagt (4.9.1):

„rvalue eines Gleitkomma Typs rvalue einen Integer-Typen umgewandelt wird, kann die Umwandlung abschneidet, das heißt, ist der Bruchteil verworfen..Das Verhalten ist undefiniert, wenn der abgeschnittene Wert nicht im Zieltyp dargestellt werden kann. "

Wenn Sie also ein double in ein int konvertieren, liegt die Zahl innerhalb des Bereichs von int und die erforderliche Aufrundung ist in Richtung Null. dann ist es genug, um die Zahl int einfach zu werfen:

(int) x;

+0

Trunkierung des Bruchteils ist nicht Boden - das Ergebnis ist für negative Zahlen unterschiedlich. – Suma

+0

Ich habe klar geschrieben "So, wenn ... die erforderliche Aufrundung gegen Null ist", um diesen Fall von "Aufrundung bis zur Unendlichkeit" zu unterscheiden. Der Boden rundet in Richtung Unendlichkeit, Abschneiden gegen Null ab. –

Verwandte Themen