Ich benötige eine einfache Gleitkomma-Rundungsfunktion, also:
double round(double);
round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1
Ich kann ceil()
und floor()
in der math.h finden - aber nicht round()
.
Ist es in der Standard-C ++ - Bibliothek unter einem anderen Namen vorhanden oder fehlt es?
c++
floating-point
rounding
Roddy
quelle
quelle
std::cout << std::fixed << std::setprecision(0) << -0.9
.round
ist seit C ++ 11 in verfügbar<cmath>
. Wenn Sie sich in Microsoft Visual Studio befinden, fehlt es leider immer noch: connect.microsoft.com/VisualStudio/feedback/details/775474/…round
viele Einschränkungen. Vor C ++ 11 stützte sich der Standard auf C90, das nicht enthalten warround
. C ++ 11 basiert auf C99, das zwar hat,round
aber auch, wie ich bereits erwähnttrunc
habe, unterschiedliche Eigenschaften hat und je nach Anwendung besser geeignet sein kann. Die meisten Antworten scheinen auch zu ignorieren, dass ein Benutzer möglicherweise einen integralen Typ zurückgeben möchte, der noch mehr Probleme aufweist.Antworten:
In der C ++ 98-Standardbibliothek gibt es kein round (). Sie können jedoch selbst eine schreiben. Das Folgende ist eine Implementierung der Aufrundung :
Der wahrscheinliche Grund dafür, dass die C ++ 98-Standardbibliothek keine Rundungsfunktion enthält, besteht darin, dass sie tatsächlich auf unterschiedliche Weise implementiert werden kann. Das Obige ist ein üblicher Weg, aber es gibt auch andere, wie z. B. Round-to-Even , die weniger voreingenommen und im Allgemeinen besser sind, wenn Sie viel runden möchten. Die Implementierung ist jedoch etwas komplexer.
quelle
Boost bietet eine einfache Reihe von Rundungsfunktionen.
Weitere Informationen finden Sie in der Boost-Dokumentation .
Bearbeiten : Da C ++ 11 sind
std::round
,std::lround
undstd::llround
.quelle
floor(value + 0.5)
Ansatz!floor(value + 0.5)
.floor(value + 0.5)
das überhaupt nicht naiv ist, sondern vom Kontext und der Natur abhängt von Werten, die Sie runden möchten!Der C ++ 03-Standard stützt sich auf den C90-Standard für das, was der Standard die Standard-C-Bibliothek nennt, die im Abschnitt zum Entwurf des C ++ 03-Standards (der C ++ 03 am nächsten liegende öffentlich zugängliche Standardentwurf ist N1804 ) behandelt wird.
1.2
Normative Verweise :Wenn wir zur C-Dokumentation für round, lround, llround on cppreference gehen , können wir sehen, dass round und verwandte Funktionen Teil von C99 sind und daher in C ++ 03 oder früher nicht verfügbar sind.
In C ++ 11 ändert sich dies, da C ++ 11 auf dem C99-Standardentwurf für die C-Standardbibliothek basiert und daher std :: round und für integrale Rückgabetypen std :: lround, std :: llround bereitstellt :
Eine andere Option auch von C99 wäre std :: trunc, die:
Wenn Sie Nicht-C ++ 11-Anwendungen unterstützen müssen, verwenden Sie am besten Boost Round, Iround, Lround, Llround oder Boost Trunc .
Es ist schwer, eine eigene Version der Runde zu rollen
Das Rollen Ihres eigenen ist wahrscheinlich nicht die Mühe wert, da es schwieriger ist, als es aussieht: Rundungsfloat auf die nächste Ganzzahl, Teil 1 , Rundungsfloat auf die nächste Ganzzahl, Teil 2 und Rundungsfloat auf die nächste Ganzzahl, Teil 3 erklären:
Beispielsweise funktioniert eine gemeinsame Rolle, die Ihre Implementierung verwendet
std::floor
und hinzufügt0.5
, nicht für alle Eingaben:Eine Eingabe, bei der dies fehlschlägt, ist
0.49999999999999994
( live sehen ).Eine andere übliche Implementierung besteht darin, einen Gleitkommatyp in einen integralen Typ umzuwandeln, was zu undefiniertem Verhalten führen kann, wenn der integrale Teil im Zieltyp nicht dargestellt werden kann. Wir können dies aus dem Entwurf des C ++ - Standardabschnitts für
4.9
Floating-Integral-Konvertierungen ersehen, der besagt ( Hervorhebung von mir ):Beispielsweise:
Gegeben
std::numeric_limits<unsigned int>::max()
ist4294967295
dann folgender Aufruf:wird einen Überlauf verursachen ( live sehen ).
Wir können sehen, wie schwierig dies wirklich ist, wenn wir uns diese Antwort auf den prägnanten Weg ansehen , um round () in C zu implementieren. welche referenzierende Newlibs- Version von Single Precision Float Round. Es ist eine sehr lange Funktion für etwas, das einfach erscheint. Es ist unwahrscheinlich, dass jemand ohne genaue Kenntnisse über Gleitkommaimplementierungen diese Funktion korrekt implementieren kann:
Wenn andererseits keine der anderen Lösungen verwendbar ist, könnte newlib möglicherweise eine Option sein, da es sich um eine gut getestete Implementierung handelt.
quelle
round(-0.0)
. C-Spezifikation scheint nicht zu spezifizieren. Ich würde-0.0
als Ergebnis erwarten .std::rint()
oft vorzuziehen ist,std::round()
wenn C ++ 11 verfügbar ist. Im Gegensatzround()
zum Spezialmodus wird der aktuelle Rundungsmodus verwendet . Es kann auf x86 viel effizienter sein, worint
es in eine einzelne Anweisung integriert werden kann. (gcc und clang tun dies auch ohne-ffast-math
godbolt.org/g/5UsL2e , während nur clang das nahezu Äquivalent einfügt.nearbyint()
) ARM unterstützt nur einzelne Anweisungenround()
, auf x86 kann es jedoch nur mit mehreren Anweisungen und nur mit-ffast-math
Es kann erwähnenswert sein, dass Sie, wenn Sie ein ganzzahliges Ergebnis aus der Rundung wünschen, es weder durch die Decke noch durch den Boden führen müssen. Dh
quelle
Es ist seit C ++ 11 in cmath verfügbar (laut http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf ).
Ausgabe:
quelle
lround
undllround
für integrale Ergebnisselrint
um den aktuellen Rundungsmodus anstelle desround
funky Tiebreak von Null zu verwenden.Es wird normalerweise als implementiert
floor(value + 0.5)
.Bearbeiten: und es wird wahrscheinlich nicht rund genannt, da mir mindestens drei Rundungsalgorithmen bekannt sind: auf Null runden, auf die nächste ganze Zahl runden und die Rundung des Bankiers. Sie fragen nach einer runden bis nächsten Ganzzahl.
quelle
Wir betrachten zwei Probleme:
Rundungsumwandlungen bedeuten Rundung ± Float / Double auf die nächste Etage / Ceil Float / Double. Vielleicht endet Ihr Problem hier. Wenn jedoch erwartet wird, dass Sie Int / Long zurückgeben, müssen Sie eine Typkonvertierung durchführen. Daher kann das Problem "Überlauf" Ihre Lösung treffen. Überprüfen Sie Ihre Funktion auf Fehler
von: http://www.cs.tut.fi/~jkorpela/round.html
quelle
LONG_MIN-0.5
undLONG_MAX+0.5
Einführen von Komplikationen, da die Mathematik möglicherweise nicht genau ist.LONG_MAX
kann diedouble
Genauigkeit für eine genaue Konvertierung überschreiten . Weitere wollen wahrscheinlichassert(x < LONG_MAX+0.5);
(<vs <=), daLONG_MAX+0.5
sie genau darstellbar sind und(x)+0.5
ein genaues Ergebnis haben können,LONG_MAX+1
dessenlong
Besetzung fehlschlägt . Andere Eckprobleme auch.round(double)
, es gibt bereits eine Standardfunktion für die Mathematikbibliothek mit diesem Namen (in C ++ 11), daher ist sie verwirrend. Verwenden Sie,std::lrint(x)
wenn es verfügbar ist.Eine bestimmte Art der Rundung ist auch in Boost implementiert:
Beachten Sie, dass dies nur funktioniert, wenn Sie eine Ganzzahlkonvertierung durchführen.
quelle
boost:numeric::RoundEven< double >::nearbyint
direkt verwenden, wenn Sie keine Ganzzahl möchten. @ DanielWolf beachten Sie, dass die einfache Funktion mit +0.5 implementiert wird, die Probleme hat, wie von aka.niceSie können die Genauigkeit auf n Stellen runden mit:
quelle
int
. (In der Praxis auf x86, out-of-Range - FP - Werte machenCVTTSD2SI
produzieren0x80000000
als der ganzzahlige Bitmuster, das heißtINT_MIN
, die dann zurück umgewandelt werdendouble
.Heutzutage sollte es kein Problem sein, einen C ++ 11-Compiler zu verwenden, der eine C99 / C ++ 11-Mathematikbibliothek enthält. Aber dann stellt sich die Frage: Welche Rundungsfunktion wählen Sie?
C99 / C ++ 11
round()
ist oft nicht die gewünschte Rundungsfunktion . Es wird ein funky Rundungsmodus verwendet, der von 0 als Gleichstand für Fälle auf halber Strecke abrundet (+-xxx.5000
). Wenn Sie diesen Rundungsmodus speziell möchten oder auf eine C ++ - Implementierung abzielen, dieround()
schneller als istrint()
, verwenden Sie sie (oder emulieren Sie ihr Verhalten mit einer der anderen Antworten auf diese Frage, die sie zum Nennwert angenommen und diese spezifisch sorgfältig reproduziert haben Rundungsverhalten.)round()
Die Rundung unterscheidet sich von der IEEE754-Standardrunde auf den nächstgelegenen Modus mit gleichmäßiger Unterbrechung . Nearest-Even vermeidet statistische Verzerrungen bei der durchschnittlichen Größe von Zahlen, tendiert jedoch zu geraden Zahlen.Es gibt zwei Rundungsfunktionen für Mathematikbibliotheken, die den aktuellen Standardrundungsmodus verwenden:
std::nearbyint()
undstd::rint()
beide wurden in C99 / C ++ 11 hinzugefügt, sodass sie jederzeit verfügbarstd::round()
sind. Der einzige Unterschied ist, dassnearbyint
FE_INEXACT niemals ausgelöst wird.Aus
rint()
Leistungsgründen bevorzugen : gcc und clang inline es leichter, aber gcc inline nienearbyint()
(auch mit-ffast-math
)gcc / clang für x86-64 und AArch64
Ich habe einige Testfunktionen in den Compiler-Explorer von Matt Godbolt gestellt , in denen Sie die Ausgabe von source + asm sehen können (für mehrere Compiler). Weitere Informationen zum Lesen der Compiler-Ausgabe finden Sie in diesen Fragen und Antworten sowie in Matts CppCon2017-Vortrag: „Was hat mein Compiler in letzter Zeit für mich getan? Aufschrauben des Compilerdeckels “ ,
Im FP-Code ist es normalerweise ein großer Gewinn, kleine Funktionen zu integrieren. Insbesondere unter Nicht-Windows, wo die Standardaufrufkonvention keine aufruferhaltenen Register enthält, kann der Compiler keine FP-Werte in XMM-Registern über a speichern
call
. Selbst wenn Sie asm nicht wirklich kennen, können Sie leicht erkennen, ob es sich nur um einen Tail-Call für die Bibliotheksfunktion handelt oder ob es sich um eine oder zwei mathematische Anweisungen handelt. Alles, was in eine oder zwei Anweisungen eingefügt wird, ist besser als ein Funktionsaufruf (für diese bestimmte Aufgabe unter x86 oder ARM).Unter x86 kann alles, was in SSE4.1 integriert
roundsd
ist, automatisch mit SSE4.1roundpd
(oder AVXvroundpd
) vektorisiert werden . (FP-> Integer-Konvertierungen sind auch in gepackter SIMD-Form verfügbar, mit Ausnahme von FP-> 64-Bit-Integer, für die AVX512 erforderlich ist.)std::nearbyint()
::-msse4.1
.-msse4.1 -ffast-math
und nur auf gcc 5.4 und früher . Später nie gcc inlines es (vielleicht haben sie nicht , dass einer der unmittelbaren Bits realisieren können die ungenauen Ausnahme unterdrücken? Das ist , was Klirren Nutzen, aber ältere gcc verwendet die gleiche sofort wierint
wenn es funktioniert inline it)std::rint
::-msse4.1
-msse4.1
. (Ohne SSE4.1 inline zu mehreren Anweisungen)-ffast-math -msse4.1
.std::round
::-ffast-math -msse4.1
, wobei zwei Vektorkonstanten erforderlich sind.std::floor
/std::ceil
/std::trunc
-msse4.1
-msse4.1
-ffast-math -msse4.1
Rundung auf
int
/long
/long long
:Hier haben Sie zwei Möglichkeiten: Verwenden
lrint
(wie,rint
aber Rückgabelong
oderlong long
fürllrint
) oder Verwenden einer FP-> FP-Rundungsfunktion und anschließendes Konvertieren in einen Ganzzahltyp auf normale Weise (mit Kürzung). Einige Compiler optimieren auf die eine Weise besser als auf die andere.Beachten Sie, dass zuerst oder ->
int i = lrint(x)
konvertiert und dann die Ganzzahl auf abgeschnitten wird . Dies macht einen Unterschied für Ganzzahlen außerhalb des Bereichs: Undefiniertes Verhalten in C ++, aber gut definiert für die x86-FP -> int-Anweisungen (die der Compiler ausgibt, es sei denn, er sieht die UB zur Kompilierungszeit, während er eine konstante Weitergabe ausführt) darf Code machen, der kaputt geht, wenn er jemals ausgeführt wird).float
double
long
int
Unter x86 erzeugt eine FP-> Ganzzahlkonvertierung, die die Ganzzahl überläuft,
INT_MIN
oderLLONG_MIN
(ein Bitmuster von0x8000000
oder das 64-Bit-Äquivalent, wobei nur das Vorzeichenbit gesetzt ist). Intel nennt dies den Wert "Integer Indefinite". (Siehe dencvttsd2si
manuellen Eintrag , den SSE2-Befehl, der (mit Kürzung) skalares Doppel in vorzeichenbehaftete Ganzzahlen konvertiert. Er ist mit einem 32-Bit- oder 64-Bit-Ganzzahlziel verfügbar (nur im 64-Bit-Modus). Es gibt auch einencvtsd2si
(Konvertieren mit aktueller Rundung) mode), das ist es, was der Compiler ausgeben soll, aber leider werden gcc und clang das nicht ohne tun-ffast-math
.Beachten Sie auch, dass FP zu / von
unsigned
int / long unter x86 (ohne AVX512) weniger effizient ist. Die Konvertierung in 32-Bit ohne Vorzeichen auf einem 64-Bit-Computer ist ziemlich billig. Konvertieren Sie einfach in 64-Bit-Signatur und schneiden Sie sie ab. Ansonsten ist es deutlich langsamer.x86 klirrte mit / ohne
-ffast-math -msse4.1
:(int/long)rint
Inlines zuroundsd
/cvttsd2si
. (verpasste Optimierung zucvtsd2si
).lrint
überhaupt nicht inline.x86 gcc6.x und früher ohne
-ffast-math
: weder inlines noch inline-ffast-math
:(int/long)rint
rundet und konvertiert separat (mit 2 Gesamtanweisungen von SSE4.1 ist aktiviert, andernfalls mit einer Reihe von Code, der fürrint
ohne eingefügt istroundsd
).lrint
nicht inline.x86 gcc mit
-ffast-math
: alle Wege inline zucvtsd2si
(optimal) , keine Notwendigkeit für SSE4.1.AArch64 gcc6.3 ohne
-ffast-math
:(int/long)rint
Inlines zu 2 Anweisungen.lrint
nicht inline-ffast-math
:(int/long)rint
Kompiliert zu einem Aufruf vonlrint
.lrint
nicht inline. Dies kann eine verpasste Optimierung sein, es sei denn, die beiden Anweisungen, auf die wir verzichten,-ffast-math
sind sehr langsam.quelle
rint()
wo dies eine praktikable Wahl ist, was normalerweise der Fall ist. Ich denke, der Nameround()
impliziert für einige Programmierer, dass dies das ist, was sie wollen, währendrint()
es mysteriös erscheint. Beachten Sie, dassround()
kein "funky" -Rundungsmodus verwendet wird: "Round-to-Next-Tie-Away" ist ein offizieller IEEE-754 (2008) -Rundungsmodus. Es ist merkwürdig, dassnearbyint()
es nicht inline wird, da es weitgehend dasselberint()
ist und unter bestimmten Bedingungen identisch sein sollte-ffast-math
. Das sieht für mich bug-ish aus.Vorsicht vor
floor(x+0.5)
. Folgendes kann für ungerade Zahlen im Bereich [2 ^ 52,2 ^ 53] passieren:Dies ist http://bugs.squeak.org/view.php?id=7134 . Verwenden Sie eine Lösung wie die von @konik.
Meine eigene robuste Version wäre ungefähr so:
Ein weiterer Grund zur Vermeidung von Boden (x + 0,5) wird hier angegeben .
quelle
Wenn Sie letztendlich die
double
Ausgabe Ihrerround()
Funktion in eine konvertieren möchtenint
, sehen die akzeptierten Lösungen dieser Frage ungefähr so aus:Dies erreicht auf meinem Computer ungefähr 8,88 ns , wenn es in gleichmäßig zufälligen Werten übergeben wird.
Das Folgende ist, soweit ich das beurteilen kann, funktional gleichwertig, taktet jedoch auf meinem Computer mit 2,48 ns , um einen signifikanten Leistungsvorteil zu erzielen:
Zu den Gründen für die bessere Leistung gehört die übersprungene Verzweigung.
quelle
int
. (In der Praxis auf x86, out-of-Range - FP - Werte machenCVTTSD2SI
produzieren0x80000000
als der ganzzahlige Bitmuster, das heißtINT_MIN
, die dann zurück umgewandelt werdendouble
.Es ist nicht erforderlich, etwas zu implementieren, daher bin ich mir nicht sicher, warum so viele Antworten Definitionen, Funktionen oder Methoden beinhalten.
In C99
Wir haben das folgende und und den Header <tgmath.h> für typgenerische Makros.
Wenn Sie dies nicht kompilieren können, haben Sie wahrscheinlich die Mathematikbibliothek ausgelassen. Ein ähnlicher Befehl funktioniert auf jedem C-Compiler, den ich habe (mehrere).
In C ++ 11
Wir haben die folgenden und zusätzlichen Überladungen in #include <cmath>, die auf IEEE-Gleitkommazahlen mit doppelter Genauigkeit beruhen.
Es gibt auch Entsprechungen im Standard-Namespace .
Wenn Sie dies nicht kompilieren können, verwenden Sie möglicherweise die C-Kompilierung anstelle von C ++. Der folgende grundlegende Befehl erzeugt weder Fehler noch Warnungen mit g ++ 6.3.1, x86_64-w64-mingw32-g ++ 6.3.0, clang-x86_64 ++ 3.8.0 und Visual C ++ 2015 Community.
Mit ordinaler Teilung
Wenn zwei Ordnungszahlen geteilt werden, wobei T kurz, int, lang oder eine andere Ordnungszahl ist, ist dies der Rundungsausdruck.
Richtigkeit
Es besteht kein Zweifel, dass bei Gleitkommaoperationen seltsam aussehende Ungenauigkeiten auftreten. Dies ist jedoch nur dann der Fall, wenn die Zahlen angezeigt werden, und hat wenig mit Rundung zu tun.
Die Quelle ist nicht nur die Anzahl der signifikanten Stellen in der Mantisse der IEEE-Darstellung einer Gleitkommazahl, sondern hängt auch mit unserem Dezimaldenken als Mensch zusammen.
Zehn ist das Produkt von fünf und zwei, und fünf und zwei sind relativ prim. Daher können die IEEE-Gleitkomma-Standards möglicherweise nicht perfekt als Dezimalzahlen für alle binären digitalen Darstellungen dargestellt werden.
Dies ist bei den Rundungsalgorithmen kein Problem. Es ist die mathematische Realität, die bei der Auswahl der Typen und der Gestaltung der Berechnungen, der Dateneingabe und der Anzeige von Zahlen berücksichtigt werden sollte. Wenn eine Anwendung die Ziffern anzeigt, die diese Probleme mit der Dezimal-Binär-Konvertierung anzeigen, drückt die Anwendung visuell Genauigkeit aus, die in der digitalen Realität nicht vorhanden ist und geändert werden sollte.
quelle
Funktion
double round(double)
mit der Verwendung dermodf
Funktion:Um sauber zu kompilieren, sind "math.h" und "Limits" erforderlich. Die Funktion arbeitet nach folgendem Rundungsschema:
quelle
rint()
oder langsamer seinnearbyint()
, aber wenn Sie wirklich keinen Compiler verwenden können, der eine ordnungsgemäße Rundungsfunktion bietet, und Sie mehr Präzision als Leistung benötigen ...Wenn Sie in der Lage sein müssen, Code in Umgebungen zu kompilieren, die den C ++ 11-Standard unterstützen, aber auch in Umgebungen, die ihn nicht unterstützen, denselben Code kompilieren müssen, können Sie ein Funktionsmakro verwenden, um zwischen std zu wählen :: round () und eine benutzerdefinierte Funktion für jedes System. Übergeben Sie einfach
-DCPP11
oder/DCPP11
an den C ++ 11-kompatiblen Compiler (oder verwenden Sie die integrierten Versionsmakros) und erstellen Sie einen Header wie den folgenden:Ein kurzes Beispiel finden Sie unter http://ideone.com/zal709 .
Dies entspricht std :: round () in Umgebungen, die nicht C ++ 11-kompatibel sind, einschließlich der Beibehaltung des Vorzeichenbits für -0.0. Dies kann jedoch zu einem leichten Leistungseinbruch führen und wahrscheinlich Probleme beim Runden bestimmter bekannter "Problem" -Gleitkommawerte wie 0,49999999999999994 oder ähnlicher Werte verursachen.
Wenn Sie Zugriff auf einen C ++ 11-kompatiblen Compiler haben, können Sie alternativ einfach std :: round () aus dem
<cmath>
Header holen und daraus einen eigenen Header erstellen, der die Funktion definiert, sofern dieser noch nicht definiert ist. Beachten Sie jedoch, dass dies möglicherweise keine optimale Lösung ist, insbesondere wenn Sie für mehrere Plattformen kompilieren müssen.quelle
Basierend auf der Antwort von Kalaxy ist das Folgende eine Vorlagenlösung, die jede Gleitkommazahl basierend auf der natürlichen Rundung auf den nächsten ganzzahligen Typ rundet. Im Debug-Modus wird auch ein Fehler ausgegeben, wenn der Wert außerhalb des Bereichs des Integer-Typs liegt, wodurch er in etwa als funktionsfähige Bibliotheksfunktion dient.
quelle
0.5
funktioniert das Hinzufügen nicht in allen Fällen. Obwohl Sie sich zumindest mit dem Überlaufproblem befassen, vermeiden Sie undefiniertes Verhalten.Wie in Kommentaren und anderen Antworten erwähnt, wurde die ISO C ++ - Standardbibliothek
round()
erst nach ISO C ++ 11 hinzugefügt , als diese Funktion unter Bezugnahme auf die ISO C99-Standardmathematikbibliothek aufgerufen wurde.Für positive Operanden in [½, ub ]
round(x) == floor (x + 0.5)
, wo ub 2 23 fürfloat
bei IEEE-754 kartiert (2008)binary32
, und 2 52 zu ,double
wenn es zu IEEE-754 abgebildet wird (2008)binary64
. Die Zahlen 23 und 52 entsprechen der Anzahl der gespeicherten Mantissenbits in diesen beiden Gleitkommaformaten. Für positive Operanden in [+0, ½)round(x) == 0
und für positive Operanden in ( ub , + ∞]round(x) == x
. Da die Funktion symmetrisch zur x-Achse ist, können negative Argumentex
entsprechend behandelt werdenround(-x) == -round(x)
.Dies führt zu dem folgenden kompakten Code. Es wird zu einer angemessenen Anzahl von Maschinenanweisungen auf verschiedenen Plattformen kompiliert. Ich habe den kompaktesten Code auf GPUs beobachtet, für
my_roundf()
den etwa ein Dutzend Anweisungen erforderlich sind. Abhängig von der Prozessorarchitektur und der Toolchain kann dieser Gleitkomma-basierte Ansatz entweder schneller oder langsamer sein als die ganzzahlige Implementierung von newlib, auf die in einer anderen Antwort verwiesen wird .Ich habe die Intel-Implementierung mit Intel Compiler Version 13 mit und
my_roundf()
ausführlich getestet . Ich habe auch überprüft, ob die newlib-Version mit der in der Bibliothek des Intel-Compilers übereinstimmt . Ausführliche Tests sind bei doppelter Genauigkeit nicht möglich , der Code ist jedoch strukturell identisch mit der Implementierung mit einfacher Genauigkeit.roundf()
/fp:strict
/fp:fast
roundf()
mathimf
round()
quelle
int
wird , dass sie mehr als 16 Bit breit ist. Es wird natürlich immer noch davon ausgegangen, dassfloat
es sich um 4-Byte-IEEE754-Binär32 handelt. Ein C ++ 11static_assert
oder vielleicht ein Makro#ifdef
/#error
könnte das überprüfen. (Aber wenn C ++ 11 verfügbar ist, sollten Sie es natürlich verwendenstd::round
oder für den aktuellen Rundungsmodus verwenden,std::rint
der gut zu gcc und clang passt.)gcc -ffast-math -msse4.1
Inlinesstd::round()
zu einemadd( AND(x, L1), OR(x,L2)
und dann zu einemroundsd
. dh es implementiert ziemlich effizientround
in Bezug aufrint
. Es gibt jedoch keinen Grund, dies manuell in C ++ zu tun, denn wenn Sie habenstd::rint()
oderstd::nearbyint()
auch habenstd::round()
. Siehe meine Antwort für einen Godbolt-Link und einen Überblick darüber, was mit verschiedenen gcc / clang-Versionen inline ist oder nicht.round()
effizient implementiertrint()
(wenn letzterer im Modus "Round-to-Nearest-or-Even" arbeitet): Ich habe das für die CUDA-Standard-Mathematikbibliothek implementiert. Diese Frage schien jedoch zu fragen, wieround()
mit C ++ vor C ++ 11 implementiert werdenrint()
soll,floor()
und wäre daher auch nicht verfügbar, nur undceil()
.round()
wird leichtrint()
im Round-to-Zero- Modus synthetisiert , auch bekannt alstrunc()
. Hätte nicht vor dem ersten Kaffee antworten sollen.round()
; Die meisten Programmierer sind sich der Unterscheidung zwischenround()
vsrint()
und Round-to-Nearest-Even einfach nicht bewusst , wobei letztere normalerweise direkt von der Hardware bereitgestellt werden und daher effizienter sind. Ich habe dies im CUDA-Programmierhandbuch dargelegt, um Programmierer darauf aufmerksam zu machen: "Die empfohlene Methode, einen Gleitkommaoperanden mit einfacher Genauigkeit auf eine Ganzzahl zu runden, wobei das Ergebnis eine Gleitkommazahl mit einfacher Genauigkeit istrintf()
, ist nichtroundf()
".Ich verwende die folgende Implementierung von round in asm für x86-Architektur und MS VS-spezifisches C ++:
UPD: um einen doppelten Wert zurückzugeben
Ausgabe:
quelle
rint()
odernearbyint()
zu einem SSE4.1-roundsd
Befehl oder einem x87-frndint
Befehl, was viel schneller ist als die zwei Roundtrips zum Speichern / Neuladen, die erforderlich sind, um diesen Inline-Asm für Daten in einem Register zu verwenden. MSVC Inline Asm ist ziemlich schlecht für das Umschließen einzelner Anweisungen, zum Beispiel,frndint
weil es keine Möglichkeit gibt, die Eingabe in ein Register zu bekommen. Die Verwendung am Ende einer Funktion mit dem Ergebnis in istst(0)
möglicherweise zuverlässig, um die Ausgabe zurückzugeben. anscheinend ist daseax
für ganze Zahlen sicher , selbst wenn es die Funktion enthält, die den asm enthält.double
und sollte sich daherfrndint
selbst für ausgeben könnenrint()
. Wenn Ihr Compiler SSE2 verwendet,double
lohnt es sich möglicherweise nicht , ein von einem XMM-Register auf x87 und zurück zu übertragen.Der beste Weg, um einen Gleitkommawert durch "n" Dezimalstellen abzurunden, ist wie folgt mit in O (1) Zeit: -
Wir müssen den Wert um 3 Stellen abrunden, dh n = 3.So,
quelle
Es könnte eine ineffiziente schmutzige Art der Konvertierung sein, aber zum Teufel, es funktioniert lol. Und es ist gut, weil es für den tatsächlichen Schwimmer gilt. Nicht nur die Ausgabe visuell beeinflussen.
quelle
Ich war das:
quelle