Frage

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 finden ceil() Und floor() in der math.h - aber nicht round().

Ist es in der Standard-C++-Bibliothek unter einem anderen Namen vorhanden oder fehlt es?

War es hilfreich?

Lösung

Es gibt keine Runde () in der C ++ 98-Standardbibliothek. Sie können jedoch selbst eine Beurteilung schreiben. Hier finden Sie eine Implementierung von round-half-up :

double round(double d)
{
  return floor(d + 0.5);
}

Der wahrscheinliche Grund gibt es keine runde Funktion ist in der C ++ 98 Standard-Bibliothek ist, dass es in der Tat kann auf verschiedene Weise implementiert werden. Die oben ist ein üblicher Weg, aber es gibt andere, wie zum Beispiel Rund to-even , die weniger voreingenommen und in der Regel besser, wenn Sie eine Menge Runden tun werden; es ist ein wenig komplexer, obwohl zu implementieren.

Andere Tipps

Boost bietet einen einfachen Satz Rundungsfunktionen.

#include <boost/math/special_functions/round.hpp>

double a = boost::math::round(1.5); // Yields 2.0
int b = boost::math::iround(1.5); // Yields 2 as an integer

Weitere Informationen finden Sie unter Verbessern Sie die Dokumentation.

Bearbeiten:Seit C++11 gibt es sie std::round, std::lround, Und std::llround.

Der C++03-Standard basiert auf dem C90-Standard für das, was der Standard nennt Standard-C-Bibliothek was im Entwurf des C++03-Standards behandelt wird (Der C++03 am nächsten kommende öffentlich verfügbare Standardentwurf ist N1804) Abschnitt 1.2 Normative Verweisungen:

Die in Abschnitt 7 von ISO/IEC 9899: 1990 und Klausel 7 von ISO/IEC 9899/AMD.1: 1995 beschriebene Bibliothek wird im Folgenden als Standard -C -Bibliothek bezeichnet.1)

Wenn wir zum gehen C-Dokumentation für „round“, „lround“ und „llround“ auf cppreference wir können das sehen runden und verwandte Funktionen sind Teil davon C99 und wird daher in C++03 oder früher nicht verfügbar sein.

In C++11 ändert sich dies, da C++11 auf dem C99-Standardentwurf für basiert C-Standardbibliothek und bietet daher std::round und für ganzzahlige Rückgabetypen std::lround, std::llround :

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::round( 0.4 ) << " " << std::lround( 0.4 ) << " " << std::llround( 0.4 ) << std::endl ;
    std::cout << std::round( 0.5 ) << " " << std::lround( 0.5 ) << " " << std::llround( 0.5 ) << std::endl ;
    std::cout << std::round( 0.6 ) << " " << std::lround( 0.6 ) << " " << std::llround( 0.6 ) << std::endl ;
}

Eine weitere Option wäre auch von C99 std::trunc welche:

Berechnet die nächste ganze Zahl, deren Betrag nicht größer als arg ist.

#include <iostream>
#include <cmath>

int main()
{
    std::cout << std::trunc( 0.4 ) << std::endl ;
    std::cout << std::trunc( 0.9 ) << std::endl ;
    std::cout << std::trunc( 1.1 ) << std::endl ;

}

Wenn Sie Nicht-C++11-Anwendungen unterstützen müssen, verwenden Sie am besten Boost-Runde, I-Runde, L-Runde, ll-Runde oder Boost-Trunk.

Es ist schwer, seine eigene Version einer Runde zu würfeln

Es ist wahrscheinlich nicht die Mühe wert, es selbst zu rollen Harter als es aussieht:Runden von Float auf die nächste ganze Zahl, Teil 1, Float auf die nächste Ganzzahl runden, Teil 2 Und Float auf die nächste Ganzzahl runden, Teil 3 erklären:

Zum Beispiel eine gemeinsame Rolle, die Ihre Implementierung verwendet std::floor und hinzufügen 0.5 funktioniert nicht für alle Eingaben:

double myround(double d)
{
  return std::floor(d + 0.5);
}

Eine Eingabe, bei der dies fehlschlägt, ist 0.49999999999999994, (Sehen Sie es live).

Eine weitere gängige Implementierung besteht darin, einen Gleitkommatyp in einen Integraltyp umzuwandeln, was zu undefiniertem Verhalten führen kann, wenn der Integralteil nicht im Zieltyp dargestellt werden kann.Wir können dies aus dem Abschnitt zum Entwurf des C++-Standards ersehen 4.9 Floating-Integral-Konvertierungen was sagt (Hervorhebung von mir):

Ein Prvalue eines schwebenden Punkttyps kann in einen Prorue eines Ganzzahltyps umgewandelt werden.Die Konvertierung wird abgeschnitten;Das heißt, der Bruchteil wird verworfen. Das Verhalten ist undefiniert, wenn der abgeschnittene Wert im Zieltyp nicht dargestellt werden kann.[...]

Zum Beispiel:

float myround(float f)
{
  return static_cast<float>( static_cast<unsigned int>( f ) ) ;
}

Gegeben std::numeric_limits<unsigned int>::max() Ist 4294967295 dann folgender Aufruf:

myround( 4294967296.5f ) 

wird einen Überlauf verursachen, (Sehen Sie es live).

Wir können sehen, wie schwierig das wirklich ist, wenn wir uns diese Antwort anschauen Prägnante Möglichkeit, Round() in C zu implementieren? welche Referenzierung neuelibs Version des Single Precision Float Round.Es ist eine sehr lange Funktion für etwas, das einfach erscheint.Es scheint unwahrscheinlich, dass jemand ohne fundierte Kenntnisse der Gleitkomma-Implementierungen diese Funktion korrekt implementieren könnte:

float roundf(x)
{
  int signbit;
  __uint32_t w;
  /* Most significant word, least significant word. */
  int exponent_less_127;

  GET_FLOAT_WORD(w, x);

  /* Extract sign bit. */
  signbit = w & 0x80000000;

  /* Extract exponent field. */
  exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;

  if (exponent_less_127 < 23)
    {
      if (exponent_less_127 < 0)
        {
          w &= 0x80000000;
          if (exponent_less_127 == -1)
            /* Result is +1.0 or -1.0. */
            w |= ((__uint32_t)127 << 23);
        }
      else
        {
          unsigned int exponent_mask = 0x007fffff >> exponent_less_127;
          if ((w & exponent_mask) == 0)
            /* x has an integral value. */
            return x;

          w += 0x00400000 >> exponent_less_127;
          w &= ~exponent_mask;
        }
    }
  else
    {
      if (exponent_less_127 == 128)
        /* x is NaN or infinite. */
        return x + x;
      else
        return x;
    }
  SET_FLOAT_WORD(x, w);
  return x;
}

Andererseits, wenn keine der anderen Lösungen brauchbar ist newlib könnte möglicherweise eine Option sein, da es sich um eine gut getestete Implementierung handelt.

Es kann sich lohnen, unter Hinweis darauf, dass, wenn Sie eine ganze Zahl ergeben sich aus der Rundung wollte Sie nicht brauchen, es entweder durch ceil oder auf dem Boden passieren. D.h.

int round_int( double r ) {
    return (r > 0.0) ? (r + 0.5) : (r - 0.5); 
}

Es ist verfügbar, da C ++ 11 in cmath (nach http : //www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf )

#include <cmath>
#include <iostream>

int main(int argc, char** argv) {
  std::cout << "round(0.5):\t" << round(0.5) << std::endl;
  std::cout << "round(-0.5):\t" << round(-0.5) << std::endl;
  std::cout << "round(1.4):\t" << round(1.4) << std::endl;
  std::cout << "round(-1.4):\t" << round(-1.4) << std::endl;
  std::cout << "round(1.6):\t" << round(1.6) << std::endl;
  std::cout << "round(-1.6):\t" << round(-1.6) << std::endl;
  return 0;
}

Ausgabe:

round(0.5):  1
round(-0.5): -1
round(1.4):  1
round(-1.4): -1
round(1.6):  2
round(-1.6): -2

Es ist in der Regel als floor(value + 0.5) umgesetzt werden.

Edit: und es ist wahrscheinlich nicht rund genannt, da es mindestens drei Runden Algorithmen Ich weiß von: Runde auf Null, rund um nächste ganze Zahl, und Abrundung des Bankers. Sie fordern Runde nächste ganze Zahl.

Es gibt zwei Probleme, mit denen wir uns befassen:

  1. Rundungsumrechnungen
  2. Typkonvertierung.

Rundungsumrechnungen bedeuten das Runden von ± Float/Double auf den nächsten Floor/Ceil Float/Double.Möglicherweise endet Ihr Problem hier.Wenn jedoch erwartet wird, dass Sie Int/Long zurückgeben, müssen Sie eine Typkonvertierung durchführen, und daher könnte das „Überlauf“-Problem Ihre Lösung sein.Überprüfen Sie daher Ihre Funktion auf Fehler

long round(double x) {
   assert(x >= LONG_MIN-0.5);
   assert(x <= LONG_MAX+0.5);
   if (x >= 0)
      return (long) (x+0.5);
   return (long) (x-0.5);
}

#define round(x) ((x) < LONG_MIN-0.5 || (x) > LONG_MAX+0.5 ?\
      error() : ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

aus : http://www.cs.tut.fi/~jkorpela/round.html

In Boost ist auch eine bestimmte Art der Rundung implementiert:

#include <iostream>

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

template<typename T, typename S> T round2(const S& x) {
  typedef boost::numeric::conversion_traits<T, S> Traits;
  typedef boost::numeric::def_overflow_handler OverflowHandler;
  typedef boost::numeric::RoundEven<typename Traits::source_type> Rounder;
  typedef boost::numeric::converter<T, S, Traits, OverflowHandler, Rounder> Converter;
  return Converter::convert(x);
}

int main() {
  std::cout << round2<int, double>(0.1) << ' ' << round2<int, double>(-0.1) << ' ' << round2<int, double>(-0.9) << std::endl;
}

Beachten Sie, dass dies nur funktioniert, wenn Sie eine Ganzzahlkonvertierung durchführen.

Sie auf n Ziffern Präzision runden könnten mit:

double round( double x )
{
const double sd = 1000; //for accuracy to 3 decimal places
return int(x*sd + (x<0? -0.5 : 0.5))/sd;
}

Wenn Sie schließlich die double Ausgabe Ihrer round() Funktion einem int konvertieren wollen, dann werden die akzeptierten Lösungen dieser Frage wird in etwa so aussehen:

int roundint(double r) {
  return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}

Diese Uhren in bei etwa 8,88 ns auf meiner Maschine, wenn sie in gleichmäßig zufälligen Werten übergeben.

Die unten funktional äquivalent ist, soweit ich das beurteilen kann, aber Uhren bei 2,48 ns auf meiner Maschine, für einen erheblichen Leistungsvorteil:

int roundint (double r) {
  int tmp = static_cast<int> (r);
  tmp += (r-tmp>=.5) - (r-tmp<=-.5);
  return tmp;
}

Zu den Gründen für die bessere Leistung der übersprungenen Verzweigung.

In diesen Tagen sollte es kein Problem verwenden, um eine C ++ 11-Compiler sein, die einen C99 / C ++ 11-Mathematik-Bibliothek enthält. Aber dann wird die Frage: welche Rundungsfunktion nicht wählen Sie

C99 / C ++ 11 round() ist oft nicht eigentlich die Rundungsfunktion Sie wollen . Es verwendet einen flippigen Rundungsmodus, der von 0 als Tie-Break auf halben Weg Fälle (+-xxx.5000) rundet weg. Wenn Sie nicht möchten ausdrücklich, dass Modus Runden, oder Sie Targeting eine C ++ Implementierung, bei round() schneller als rint() ist, dann verwenden (oder sein Verhalten zu emulieren mit einem der anderen Antworten auf diese Frage, die sie für bare Münze nahm und sorgfältig reproduziert dass spezifische Rundungsverhalten.)

round() Rundung unterscheidet sich von der IEEE754 Standard Runde zum nächsten Modus rel="nofollow mit sogar als Tie-Break . Nearest-selbst vermeidet statistische Verzerrung in der durchschnittlichen Größe der Zahlen, sondern tut Tendenz zu geraden Zahlen.

Es gibt zwei Mathematik-Bibliothek Rundungsfunktionen, die den aktuellen Standardrundungsmodus verwenden: std::nearbyint() und std::rint() sowohl in C99 / C hinzugefügt ++ 11, so dass sie jederzeit verfügbar std::round() ist. Der einzige Unterschied besteht darin, dass nearbyint nie wirft FE_INEXACT.

Bevorzugen rint() aus Performance-Gründen : gcc und klirren beide es inline leichter, aber gcc nie inlines nearbyint() (auch mit -ffast-math)


gcc / Klirren für x86-64 und AArch64

Ich habe ein paar Testfunktionen auf Matt Godbolt der Compiler ExpLorer , wo Sie Quelle + asm-Ausgang (für mehrere Compiler) sehen. Für mehr über die Compilerausgabe lesen, finden Sie unter dieses Q & A und Matts CppCon2017 Diskussion: „Was mein Compiler hat in letzter Zeit für mich getan? Abschrauben des Deckels des Compiler“,

In FP-Code, es ist in der Regel ein großer Gewinn kleine Funktionen Inline. Vor allem auf Nicht-Windows, wo die Standard-Aufrufkonvention kein Anruf erhaltenen Register hat, so kann der Compiler keine FP-Werte in XMM halten Register über einen call. Also selbst wenn Sie nicht wirklich asm nicht kennen, können Sie immer noch leicht sehen, ob es nur ein Schwanz-Aufruf die Bibliotheksfunktion oder ob es inlined auf einer oder zwei mathematischen Anweisungen. Alles, was auf einen oder zwei Befehle inlines ist besser als ein Funktionsaufruf (für diese spezielle Aufgabe auf x86 oder ARM).

Auf x86, alles, was inlines zu SSE4.1- roundsd kann auto-vectorize mit SSE4.1- roundpd (oder AVX vroundpd). (FP-> integer Umwandlungen sind auch in gepackten SIMD Form zur Verfügung, mit Ausnahme von FP-> 64-Bit-Integer, die AVX512 erfordert.)

  • std::nearbyint():

    • x86 Klirren. Inlines zu einem einzigen insn mit -msse4.1
    • x86 gcc: inlines zu einem einzigen insn nur mit -msse4.1 -ffast-math, und nur auf gcc 5.4 und früher . Später gcc inlines es nie (vielleicht wußte sie nicht, dass einer der unmittelbaren Bits kann die ungenaue Ausnahme unterdrücken? Das ist, was Anwendungen klirrte, aber älterer gcc verwendet die gleichen sofort wie für rint wenn sie es nicht inline)
    • AArch64 gcc6.3. Inlines zu einem einzigen insn standardmäßig
  • std::rint:

    • x86 Klirren: inlines zu einem einzigen insn mit -msse4.1
    • x86 gcc7: inlines zu einem einzigen insn mit -msse4.1. (Ohne SSE4.1- inlines auf mehrere Anweisungen)
    • x86 gcc6.x und früher:. Inlines zu einem einzigen insn mit -ffast-math -msse4.1
    • AArch64 gcc: inlines zu einem einzigen insn standardmäßig
  • std::round:

    • x86 Klirren: keine Inline
    • x86 gcc. Inlines auf mehrere Befehle mit -ffast-math -msse4.1, erfordert zwei Vektor Konstanten
    • AArch64 gcc: (. HW-Unterstützung für diese Rundungsmodus sowie IEEE Standard und die meisten anderen) inlines auf einen einzigen Befehl
  • std::floor / std::ceil / std::trunc

    • x86 Klirren: inlines zu einem einzigen insn mit -msse4.1
    • x86 gcc7.x: inlines zu einem einzigen insn mit -msse4.1
    • x86 gcc6.x und früher: inlines zu einem einzigen insn mit -ffast-math -msse4.1
    • AArch64 gcc: inlines standardmäßig auf einen einzigen Befehl

Rounding int / long / long long:

Sie haben zwei Möglichkeiten: Verwenden lrint (wie rint aber kehrt long oder long long für llrint) oder eine FP- verwenden> FP Rundungsfunktion und dann auf eine ganze Zahl gibt die normale Art und Weise umwandeln (mit Abschneiden). Einige Compiler optimieren eine Art und Weise besser als die andere.

long l = lrint(x);

int  i = (int)rint(x);

Beachten Sie, dass int i = lrint(x) wandelt float oder double -> long zuerst, und dann abschneidet die ganze Zahl int. Das macht einen Unterschied für Out-of-Range-Zahlen: nicht definiertes Verhalten in C ++, aber gut definiert für die x86-FP -> int Anweisungen (die der Compiler emittieren wird, wenn es die UB bei der Kompilierung sieht während Konstantenpropagation tut, dann ist es erlaubt, um Code zu machen, dass, wenn es bricht immer ausgeführt wird).

Auf x86, ein FP-> integer Umwandlung, die die ganze Zahl überläuft erzeugt INT_MIN oder LLONG_MIN (ein Bit-Muster von 0x8000000 oder die 64-Bit-Äquivalent, mit nur dem Vorzeichen-Bit gesetzt). Intel nennt dies die „integer unbestimmt“ Wert. (Siehe die cvttsd2si manuelle Eingabe die SSE2 Anweisung, die (mit Trunkierung umwandelt) skalare Doppel unterzeichnet integer. Es ist verfügbar mit 32-Bit oder 64-Bit-integer-Ziel (in 64-Bit-Modus). Es gibt auch ein cvtsd2si (konvertieren mit aktuellem Rundungsmodus), das ist das, was wir die Compiler emittieren möchten, aber leider gcc und Klirren wird das nicht ohne -ffast-math.

Auch hüte dich, dass FP zum / vom unsigned int long / weniger effizient auf x86 (ohne AVX512). Die Umstellung auf 32-Bit ohne Vorzeichen auf einer 64-Bit-Maschine ist ziemlich billig; konvertieren nur auf 64-Bit unterzeichnet und gestutzt. Aber sonst ist es wesentlich langsamer.

  • x86 Klirren mit / ohne -ffast-math -msse4.1: (int/long)rint inlines / roundsd cvttsd2si. (Verpasste Optimierung cvtsd2si). lrint gar nicht inline.

  • x86 gcc6.x und früher ohne -ffast-math: weder Weg inlines

  • x86 gcc7 ohne -ffast-math: (int/long)rint Runden und wandelt separat (mit 2 Gesamt Anweisungen von SSE4.1- aktiviert ist, andernfalls mit einem Bündel von Code für inlined rint ohne roundsd). lrint nicht inline.
  • x86 gcc mit -ffast-math. alle Möglichkeiten inline cvtsd2si (optimal) , keine Notwendigkeit für SSE4.1-

  • AArch64 gcc6.3 ohne -ffast-math: (int/long)rint inlines bis 2 Anweisungen. lrint keine Inline

  • AArch64 gcc6.3 mit -ffast-math: (int/long)rint kompiliert zu einem Aufruf an lrint. lrint nicht inline nicht. Dies kann eine verpasste Optimierung sein, wenn die beiden Befehle wir ohne -ffast-math bekommen sind sehr langsam.

Vorsicht vor floor(x+0.5). Hier ist, was für ungerade Zahlen in Bereich passieren kann [2 ^ 52,2 ^ 53]:

-bash-3.2$ cat >test-round.c <<END

#include <math.h>
#include <stdio.h>

int main() {
    double x=5000000000000001.0;
    double y=round(x);
    double z=floor(x+0.5);
    printf("      x     =%f\n",x);
    printf("round(x)    =%f\n",y);
    printf("floor(x+0.5)=%f\n",z);
    return 0;
}
END

-bash-3.2$ gcc test-round.c
-bash-3.2$ ./a.out
      x     =5000000000000001.000000
round(x)    =5000000000000001.000000
floor(x+0.5)=5000000000000002.000000

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 so etwas wie:

double round(double x)
{
    double truncated,roundedFraction;
    double fraction = modf(x, &truncated);
    modf(2.0*fraction, &roundedFraction);
    return truncated + roundedFraction;
}

Ein weiterer Grund, Boden zu vermeiden (x + 0,5) ist hier gegebenen .

Es besteht keine Notwendigkeit, irgendetwas zu implementieren, daher bin ich mir nicht sicher, warum so viele Antworten Definitionen, Funktionen oder Methoden beinhalten.

Im C99

Wir haben den folgenden und-Header <tgmath.h> für typgenerische Makros.

#include <math.h>
double round (double x);
float roundf (float x);
long double roundl (long double x);

Wenn Sie dies nicht kompilieren können, haben Sie wahrscheinlich die Mathematikbibliothek weggelassen.Ein ähnlicher Befehl funktioniert auf jedem C-Compiler, den ich habe (mehrere).

gcc -lm -std=c99 ...

In C++11

Wir haben die folgenden und zusätzliche Überladungen in #include <cmath>, die auf IEEE-Gleitkommazahlen mit doppelter Genauigkeit basieren.

#include <math.h>
double round (double x);
float round (float x);
long double round (long double x);
double round (T x);

Es gibt Entsprechungen im std-Namespace zu.

Wenn Sie dies nicht kompilieren können, verwenden Sie möglicherweise die C-Kompilierung anstelle von C++.Der folgende Basisbefehl 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.

g++ -std=c++11 -Wall

Mit Ordinaldivision

Bei der Division zweier Ordnungszahlen, bei denen T kurz, int, lang oder eine andere Ordnungszahl ist, lautet der Rundungsausdruck wie folgt.

T roundedQuotient = (2 * integerNumerator + 1)
    / (2 * integerDenominator);

Genauigkeit

Es besteht kein Zweifel, dass bei Gleitkommaoperationen seltsam aussehende Ungenauigkeiten auftreten, aber dies geschieht nur, wenn die Zahlen erscheinen, und hat wenig mit dem Runden zu tun.

Die Quelle ist nicht nur die Anzahl der signifikanten Stellen in der Mantisse der IEEE-Darstellung einer Gleitkommazahl, sie hängt auch mit unserem Dezimaldenken als Menschen zusammen.

Zehn ist das Produkt von fünf und zwei, und 5 und 2 sind relativ prim.Daher können die IEEE-Gleitkommastandards unmöglich für alle binären digitalen Darstellungen perfekt als Dezimalzahlen dargestellt werden.

Bei den Rundungsalgorithmen ist dies kein Problem.Es ist die mathematische Realität, die bei der Auswahl der Typen und der Gestaltung von Berechnungen, der Dateneingabe und der Anzeige von Zahlen berücksichtigt werden sollte.Wenn eine Anwendung die Ziffern anzeigt, die diese Probleme bei der Dezimal-Binär-Konvertierung zeigen, dann drückt die Anwendung visuell eine Genauigkeit aus, die in der digitalen Realität nicht existiert und geändert werden sollte.

Funktion double round(double) mit der Verwendung der modf Funktion:

double round(double x)
{
    using namespace std;

    if ((numeric_limits<double>::max() - 0.5) <= x)
        return numeric_limits<double>::max();

    if ((-1*std::numeric_limits<double>::max() + 0.5) > x)
        return (-1*std::numeric_limits<double>::max());

    double intpart;
    double fractpart = modf(x, &intpart);

    if (fractpart >= 0.5)
        return (intpart + 1);
    else if (fractpart >= -0.5)
        return intpart;
    else
        return (intpart - 1);
    }

sein, um sauber zu kompilieren, enthält „math.h“ und „Grenzen“ notwendig sind. Die Funktion arbeitet nach einem folgenden Runden-Schema:

  • Runde von 5,0 ist 5,0
  • Runde 3,8 4,0
  • Runde 2,3 2,0
  • Runde 1,5 2,0
  • Runde von 0,501 1,0
  • Runde 0,5 1,0
  • Runde von 0,499 0,0
  • Runde von 0,01 ist 0,0
  • Runde 0,0 0,0
  • Runde von -0,01 ist -0.0
  • Runde von -0,499 ist -0.0
  • Runde von -0,5 ist -0.0
  • Runde von -0,501 -1,0
  • Runde von -1,5 -1,0
  • Runde von -2,3 ist -2.0
  • Runde von -3,8 ist -4.0
  • Runde von -5,0 -5,0

Wenn Sie in die Lage sein müssen Code in Umgebungen zu kompilieren, die den C ++ 11-Standard unterstützen, sondern müssen auch den gleichen Code in Umgebungen kompilieren zu können, die diese Funktion nicht unterstützen, können Sie eine Funktion Makro verwenden, um wählen zwischen std :: round () und eine benutzerdefinierte Funktion für jedes System. übergeben Sie einfach -DCPP11 oder /DCPP11 an dem C ++ 11-kompatible Compiler (oder verwenden Sie seine integrierte Version Makros), und macht dies zu einem Header wie:

// File: rounding.h
#include <cmath>

#ifdef CPP11
    #define ROUND(x) std::round(x)
#else    /* CPP11 */
    inline double myRound(double x) {
        return (x >= 0.0 ? std::floor(x + 0.5) : std::ceil(x - 0.5));
    }

    #define ROUND(x) myRound(x)
#endif   /* CPP11 */

Für ein schnelles Beispiel finden Sie unter http://ideone.com/zal709 .

Dieser annähert std :: round () in Umgebungen, die nicht C ++ 11-kompatibel sind, einschließlich der Erhaltung des Vorzeichenbits für -0.0. Es kann eine leichte Leistungseinbußen verursachen, aber, und wird wahrscheinlich Probleme haben, mit bestimmten bekannten „Problem“ Floating-Point-Werte wie 0,49999999999999994 oder ähnliche Werte gerundet wird.

Alternativ, wenn Sie Zugang zu einem C ++ 11-kompatible Compiler haben, könnten Sie nur std :: round () von seinem <cmath> Header packen, und verwenden Sie es Ihren eigenen Header zu machen, der die Funktion definiert, wenn es nicht bereits ist definiert. Beachten Sie, dass dies keine optimale Lösung sein, aber vor allem, wenn Sie für mehrere Plattformen kompilieren müssen.

Basierend auf der Antwort von Kalaxy ist das Folgende eine Vorlagenlösung, die jede Gleitkommazahl basierend auf natürlicher Rundung auf den nächsten Ganzzahltyp rundet.Außerdem wird im Debug-Modus ein Fehler ausgegeben, wenn der Wert außerhalb des Bereichs des Integer-Typs liegt, und dient somit grob als brauchbare Bibliotheksfunktion.

    // round a floating point number to the nearest integer
    template <typename Arg>
    int Round(Arg arg)
    {
#ifndef NDEBUG
        // check that the argument can be rounded given the return type:
        if (
            (Arg)std::numeric_limits<int>::max() < arg + (Arg) 0.5) ||
            (Arg)std::numeric_limits<int>::lowest() > arg - (Arg) 0.5)
            )
        {
            throw std::overflow_error("out of bounds");
        }
#endif

        return (arg > (Arg) 0.0) ? (int)(r + (Arg) 0.5) : (int)(r - (Arg) 0.5);
    }

Wie in den Kommentaren und anderen Antworten darauf hingewiesen, die ISO C ++ Standard-Bibliothek hinzufügen, nicht round() bis ISO C ++ 11, wenn diese Funktion wurde durch Bezugnahme auf die ISO C99 Standard-Mathematik-Bibliothek eingezogen.

Für positive Operanden in [½, ub ] round(x) == floor (x + 0.5), wobei ub 2 23 für float wenn IEEE-754 abgebildet (2008 ) binary32 und 2 52 für double, wenn es um IEEE-754 (2008) binary64 abgebildet wird. Die Zahlen 23 und 52 entsprechen der Anzahl von gespeichert Mantissenbits in diesen beiden Gleitkommaformate. Für positive Operanden in [+0, ½) round(x) == 0 und für positive Operanden ( ub , + ∞] round(x) == x. Da die Funktion symmetrisch um die x-Achse ist, kann negative Argumente x entsprechend behandelt werden round(-x) == -round(x) zu.

Dies führt zu dem kompakten Code unten. Es kompiliert in eine angemessene Anzahl von Maschinenbefehlen über verschiedene Plattformen. Ich beobachtete die kompakteste Code auf GPUs, wo my_roundf() etwa ein Dutzend Anweisungen erfordert. Je nach Prozessorarchitektur und Toolchain, diese Gleitkommaoperationen basierte Ansatz könnte sein, entweder schneller oder langsamer als die ganzen Zahl basierte Implementierung von newlib in einem verwiesen andere Antwort .

I getestet my_roundf() erschöpfend gegen die newlib roundf() Implementierung unter Verwendung von Intel-Compiler Version 13, mit beiden /fp:strict und /fp:fast. Ich habe auch, dass die newlib Version den roundf() in der mathimf Bibliothek der Intel-Compiler übereinstimmt. Erschöpfende Prüfung nicht möglich ist, mit doppelter Genauigkeit round(), aber der Code ist strukturell identisch mit der Single-Precision-Implementierung.

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>

float my_roundf (float x)
{
    const float half = 0.5f;
    const float one = 2 * half;
    const float lbound = half;
    const float ubound = 1L << 23;
    float a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floorf (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

double my_round (double x)
{
    const double half = 0.5;
    const double one = 2 * half;
    const double lbound = half;
    const double ubound = 1ULL << 52;
    double a, f, r, s, t;
    s = (x < 0) ? (-one) : one;
    a = x * s;
    t = (a < lbound) ? x : s;
    f = (a < lbound) ? 0 : floor (a + half);
    r = (a > ubound) ? x : (t * f);
    return r;
}

uint32_t float_as_uint (float a)
{
    uint32_t r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float uint_as_float (uint32_t a)
{
    float r;
    memcpy (&r, &a, sizeof(r));
    return r;
}

float newlib_roundf (float x)
{
    uint32_t w;
    int exponent_less_127;

    w = float_as_uint(x);
    /* Extract exponent field. */
    exponent_less_127 = (int)((w & 0x7f800000) >> 23) - 127;
    if (exponent_less_127 < 23) {
        if (exponent_less_127 < 0) {
            /* Extract sign bit. */
            w &= 0x80000000;
            if (exponent_less_127 == -1) {
                /* Result is +1.0 or -1.0. */
                w |= ((uint32_t)127 << 23);
            }
        } else {
            uint32_t exponent_mask = 0x007fffff >> exponent_less_127;
            if ((w & exponent_mask) == 0) {
                /* x has an integral value. */
                return x;
            }
            w += 0x00400000 >> exponent_less_127;
            w &= ~exponent_mask;
        }
    } else {
        if (exponent_less_127 == 128) {
            /* x is NaN or infinite so raise FE_INVALID by adding */
            return x + x;
        } else {
            return x;
        }
    }
    x = uint_as_float (w);
    return x;
}

int main (void)
{
    uint32_t argi, resi, refi;
    float arg, res, ref;

    argi = 0;
    do {
        arg = uint_as_float (argi);
        ref = newlib_roundf (arg);
        res = my_roundf (arg);
        resi = float_as_uint (res);
        refi = float_as_uint (ref);
        if (resi != refi) { // check for identical bit pattern
            printf ("!!!! arg=%08x  res=%08x  ref=%08x\n", argi, resi, refi);
            return EXIT_FAILURE;
        }
        argi++;
    } while (argi);
    return EXIT_SUCCESS;
}

Ich verwende die folgende Implementierung von Runde in asm für x86-Architektur und MS VS spezifische C ++:

__forceinline int Round(const double v)
{
    int r;
    __asm
    {
        FLD     v
        FISTP   r
        FWAIT
    };
    return r;
}

UPD: double-Wert zurück

__forceinline double dround(const double v)
{
    double r;
    __asm
    {
        FLD     v
        FRNDINT
        FSTP    r
        FWAIT
    };
    return r;
}

Ausgabe:

dround(0.1): 0.000000000000000
dround(-0.1): -0.000000000000000
dround(0.9): 1.000000000000000
dround(-0.9): -1.000000000000000
dround(1.1): 1.000000000000000
dround(-1.1): -1.000000000000000
dround(0.49999999999999994): 0.000000000000000
dround(-0.49999999999999994): -0.000000000000000
dround(0.5): 0.000000000000000
dround(-0.5): -0.000000000000000

bester Weg, einen schwimmenden Wert von „n“ Dezimalstellen abrundet, ist wie folgend mit in O (1) Zeit: -

Wir haben den Wert von 3 Plätze abzurunden das heißt n = 3.So,

float a=47.8732355;
printf("%.3f",a);
// Convert the float to a string
// We might use stringstream, but it looks like it truncates the float to only
//5 decimal points (maybe that's what you want anyway =P)

float MyFloat = 5.11133333311111333;
float NewConvertedFloat = 0.0;
string FirstString = " ";
string SecondString = " ";
stringstream ss (stringstream::in | stringstream::out);
ss << MyFloat;
FirstString = ss.str();

// Take out how ever many decimal places you want
// (this is a string it includes the point)
SecondString = FirstString.substr(0,5);
//whatever precision decimal place you want

// Convert it back to a float
stringstream(SecondString) >> NewConvertedFloat;
cout << NewConvertedFloat;
system("pause");

Es könnte ein ineffizienter schmutzig Weg der Umkehr sein, aber was solls, es funktioniert lol. Und es ist gut, weil es auf den tatsächlichen Schwimmer gilt. Nicht nur die Ausgabe optisch beeinflussen.

Ich tue dies:

#include <cmath.h>

using namespace std;

double roundh(double number, int place){

    /* place = decimal point. Putting in 0 will make it round to whole
                              number. putting in 1 will round to the
                              tenths digit.
    */

    number *= 10^place;
    int istack = (int)floor(number);
    int out = number-istack;
    if (out < 0.5){
        floor(number);
        number /= 10^place;
        return number;
    }
    if (out > 0.4) {
        ceil(number);
        number /= 10^place;
        return number;
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top