2017-11-07 3 views
4

Ich habe eine Frage zu std::round mit Unterschrift: double round (double x);Ist es sicher, von std :: round nach int zu casten?

Sagen wir, ich diesen Code haben:

int i = std::round(0.9); 

In diesem Fall std::round sollte 1.00000000000 zurückkehren, aber das ist unangenehm nah an 0.9999999999999 und ich bin besorgt dass Fließkommafehler am Ende das Ergebnis abrunden.

Ich würde hoffen, dass i == 1, aber ist das garantiert?

+6

http://en.cppreference.com/w/cpp/numeric/math/rint – Mat

+1

@Mat soweit ich sehen kann ist der einzige Unterschied, dass 'runde' den aktuellen Rundungsmodus explizit nicht respektiert. Die Garantien sind ansonsten gleich. – BoBTFish

+1

@BoBTFish: Die 'lrint'-Varianten liefern ganzzahlige Werte, keine ganzzahligen Werte im Fließkommaformat. Nicht ganz das gleiche (soweit ich es verstehe) – Mat

Antwort

6

Die Funktion std::round gibt einen Fließkommawert zurück, "Rundung halbwegs von Null entfernt". Wie bei jedem double zu int implizite Konvertierung wird Thema der Compiler eine Warnung:

Umwandlung von ‚double‘ auf ‚int‘, möglichen Datenverlust

Verwenden std::lround oder std::lrint, wenn Sie zurückkehren wollen ein ganzzahliger Wert.

+1

'rint' scheint genau die gleichen Garantien wie' round' zu haben, außer dass der aktuell eingestellte Rundungsmodus eingehalten wird. – BoBTFish

+1

@BoBTFish Ich habe die Antwort mit der 'std :: lrint' Variante aktualisiert. – Ron

+0

Dies beantwortet die Frage nicht explizit (und auch nicht die anderen Antworten). Fließkommafehler treten nicht zufällig oder willkürlich auf; Ein Wert von genau 1, der von 'std :: round 'zurückgegeben wird, wird nicht willkürlich in etwas anderes geändert. Technisch bieten die C- und C++ - Standards keine Garantie für verschiedene Aspekte des Fließkommaverhaltens, aber keine normale Implementierung würde einen Fehler bei der Umwandlung des (mathematischen) ganzzahligen Ergebnisses von "std :: round" in ein "int" haben Das Ergebnis liegt im Bereich von 'int'. –

5

Der C++ - Standard verweist dafür auf den C-Standard. In N1570 (~ C11), ist die Beschreibung der round wie folgt:

Die round Funktionen runden ihr Argument auf den nächsten ganzzahligen Wert im Gleitkommaformat, auf halbe Strecke Fälle weg von Null rundet, und zwar unabhängig von den aktuellen Rundungs Richtung.

Allerdings, wie Ron darauf hingewiesen, Funktionen wie lrint tun genau das, was Sie wollen.

+2

"Beachten Sie, dass dies möglicherweise nicht die nächste ganze Zahl ist" Das ist falsch. Es wird sein. Wenn die Zahl keine ganze Zahl ist, sind beide Ganzzahlen in der Umgebung verfügbar. Wenn es eine Ganzzahl ist, dann gibt es nichts zu tun. – geza

+0

@geza Absolut korrekt, natürlich. Dieser Teil wurde entfernt. – BoBTFish

1

Nach cppreference:

Gleitpunktarithmetik integrales Konvertierungen

A prvalue von Fließkommatyp kann zu einer prvalue jeden Integer-Typs umgewandelt werden. Der Bruchteil ist abgeschnitten, das heißt, der Bruchteil wird verworfen. Wenn der Wert nicht in den Zieltyp passen kann, ist das Verhalten nicht definiert (selbst wenn der Zieltyp nicht vorzeichenbehaftet ist, gilt die Modulo-Arithmetik nicht). Wenn der Zieltyp bool ist, handelt es sich um eine boolesche Konvertierung (siehe unten).

+0

Die Frage scheint zu sein, ist es sicher, "rund" zu verwenden, dann mit dieser Kürzung zu folgen? I.e. ist 'rund' garantiert, etwas so zurückzugeben, dass das Ergebnis der Kürzung mathematisch den gleichen Wert ergibt? – BoBTFish

-1

wie etwa 0,5 Hinzufügen und dann int Gießen (Kürzen)

if(x < 0) { //number is negative 
return (int) x - 0.5; 
} else { 
return (int) x + 0.5;//number is positive 
} 

in Ihrem Beispiel soll es 1 zurückkehren sicher weil 1,49999999999 cast int 1

+1

Sehr sehr ungezogen; Dieser Ansatz schlägt in einigen Randfällen fehl. Java hatte diesen Fehler bis 1.5. Außerdem haben Sie die Priorität Ihres Operators durcheinander gebracht. Sie werfen auf ein "int", ** und fügen dann die Konstante hinzu! – Bathsheba

6

Ja. Solange Sie den Empfangstyp nicht überlaufen.

Sie haben keine Bedenken hier: std::round(0.9)-genau1.0 und so abzurunden wird wird i == 1

Der Standard besteht auf dem am nächsten Integralwert assumable von einem double zurückgegeben garantiert.

Beachten Sie jedoch, dass für einen IEEE754 double alle Werte über die 52. Potenz von 2 Integralwerte sind! Eine logische Folge davon ist, dass Ihre Kandidatennummer für die Rundung bereits ein ganzzahliger Wert ist, so dass die Funktion zu einem No-Op reduziert wird. Die Tatsache, dass zum Beispiel std::round(4503599627370496.5)4503599627370496 zurückgibt, hat damit zu tun, dass 4503599627370496.5 in erster Linie nicht als double dargestellt werden kann.

Als letzten technischen Punkt zu beachten, dass std::round bemerkenswert gut erzogene ist, aufgrund der Tatsache, teilweise, dass jede Zahl der Form a.5 (das ist der cutover Punkt der deutschen Rundung) eine dyadischen rationalen ist und so kann im binären Gleitkomma genau dargestellt werden. Dies ist der Grund, warum ein alternativer Ansatz, wie z. B. das Hinzufügen von 0.5 und das Abschneiden, Bugs einbringen kann, da in diesem Fall Witzziffern eingeführt werden können.

Verwandte Themen