Prägnante Möglichkeit, round() in C zu implementieren?

Lesezeit: 7 Minuten

Benutzer-Avatar
Fred Bassett

Das eingebettete C, das ich verwende, hat keine round()-Funktion, es ist eine mathematische Bibliothek, was wäre eine prägnante Möglichkeit, dies in C zu implementieren? Ich dachte daran, es in eine Zeichenfolge zu drucken, nach der Dezimalstelle zu suchen, dann das erste Zeichen nach dem Punkt zu finden und dann aufzurunden, wenn> = 5, sonst nach unten. usw. Habe mich gefragt, ob es etwas Clevereres gibt.

Danke Fred

  • Beachten Sie, dass round() ist in C99, also nicht unbedingt in allen C-Bibliotheken.

    – Chrisaycock

    31. Dezember 2010 um 22:32 Uhr

  • Sind Boden- oder Deckenfunktionen verfügbar?

    – Kratylus

    31. Dezember 2010 um 22:37 Uhr

  • Runden ist ziemlich sinnlos, nur Menschen kümmern sich darum. Die einzigen, die etwas ungeduldig werden, wenn sie 7 oder 15 Ziffern im Ergebnis lesen müssen. Der Maschine ist das egal, sie wird nicht müde. Kein Problem, printf() rundet recht gut. Nur für den Fall: Implementieren Sie kein Buchhaltungsprogramm in C, ohne eine Bibliothek zu haben, die in Basis 10 rechnet.

    – Hans Passant

    31. Dezember 2010 um 23:09 Uhr


  • Aus Neugier, welcher vorsintflutlichen Mathematikbibliothek werden Sie ausgesetzt?

    – Stefan Kanon

    1. Januar 2011 um 3:13 Uhr


  • @HansPassant Reality kümmert sich darum, wenn Sie die Zeiten berechnen, die etwas getan werden muss, oder die Anzahl der Teile, die Sie benötigen. Das ist nicht ganz sinnlos.

    – Amin Negm-Awad

    31. März 2017 um 6:54 Uhr

Benutzer-Avatar
Brooks Moses

Sie könnten das Rad neu erfinden, wie viele andere Antworten vermuten lassen. Alternativ könnten Sie das Rad eines anderen verwenden – ich würde das von Newlib vorschlagen, das BSD-lizenziert und für die Verwendung auf eingebetteten Systemen vorgesehen ist. Es verarbeitet negative Zahlen, NaNs, Unendlichkeiten und Fälle, die nicht als ganze Zahlen darstellbar sind (weil sie zu groß sind), ordnungsgemäß und auf effiziente Weise, indem Exponenten und Maskierung anstelle von allgemein kostspieligeren Gleitkommaoperationen verwendet werden. Darüber hinaus wird es regelmäßig getestet, sodass Sie wissen, dass es keine eklatanten Eckfehler enthält.

Die Navigation in der Newlib-Quelle kann etwas umständlich sein, also hier sind die Teile, die Sie wollen:

Float-Version:
https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/sf_round.c;hb=master

Doppelversion:
https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/s_round.c;hb=master

Hier definierte Wortextraktionsmakros:
https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=newlib/libm/common/fdlibm.h;hb=master

Wenn Sie andere Dateien von dort benötigen, ist das übergeordnete Verzeichnis dieses:
https://sourceware.org/git/gitweb.cgi?p=newlib-cygwin.git;a=tree;f=newlib/libm/common;hb=master

Fürs Protokoll, hier ist der Code für die Float-Version. Wie Sie sehen können, ist ein wenig Komplexität erforderlich, um alle möglichen Fälle korrekt zu behandeln.

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;
}

  • +1 Dem stimme ich voll und ganz zu, in meiner Antwort auf eine C++-Version dieser Frage hier habe ich auf einen dreiteiligen Artikel verlinkt, warum das Rollen Ihrer eigenen Rundung so schwierig ist.

    – Shafik Yaghmour

    27. Juni 2014 um 2:17 Uhr

  • Es könnte schön sein, einige aktualisierte Links zu erhalten, diese Links gehen jetzt einfach auf die oberste Ebene.

    – LovesTha

    6. Juni 2016 um 0:49 Uhr

  • Zumal der eingefügte Code auf Makros angewiesen ist, die sich hinter Links befinden, die nicht funktionieren 🙂

    – LovesTha

    6. Juni 2016 um 0:50 Uhr

  • Danke an LThode@ für das Korrigieren der Links!

    – Brooks Moses

    12. April 2017 um 0:01 Uhr


  • Warum weist es die signbit Variable? Es wird später nicht verwendet.

    – Hockendes Kätzchen

    28. Mai 2017 um 18:46 Uhr


int round(double x)
{
    if (x < 0.0)
        return (int)(x - 0.5);
    else
        return (int)(x + 0.5);
}

  • Die C-Bibliotheksfunktion round( ) gibt ein Double zurück, kein Int.

    – Stefan Kanon

    31. Dezember 2010 um 23:11 Uhr

  • Beachten Sie auch, dass diese Implementierung ein undefiniertes Verhalten für hat x außerhalb des durch ganze Zahlen darstellbaren Wertebereichs.

    – Stefan Kanon

    31. Dezember 2010 um 23:28 Uhr

  • Versuchen Sie den obigen Code auf 0.49999999999999994; auf Implementierungen, die 64-Bit verwenden double Bei Berechnungen ergibt das Hinzufügen von 0,5 zu diesem Wert 1,0, obwohl abgerundet werden sollte.

    – Superkatze

    19. März 2015 um 19:03 Uhr

  • @supercat: Ich glaube, das ist beabsichtigt.

    – Daniel

    31. Mai 2016 um 10:27 Uhr

  • @Daniel: Wie ist round spezifizierten? Lässt die Spezifikation zu, dass ein Wert, der definitiv unter 0,5 liegt, auf 1,0 aufgerundet wird?

    – Superkatze

    31. Mai 2016 um 14:32 Uhr

Benutzer-Avatar
nmichaels

int round(float x)
{
    return (int)(x + 0.5);
}

Vorbehalt: Funktioniert nur bei positiven Zahlen.

  • Sie vergessen negative Zahlen: Runde (-1,3) = 0 in Ihrem Fall.

    – Niki Yoshiuchi

    31. Dezember 2010 um 22:43 Uhr

  • @Niki: Ich vergesse sie nicht; Ich entschied mich einfach, sie zu ignorieren. Das Hinzufügen von Unterstützung für sie ist trivial (siehe Antwort von dan04), und mein Ziel war es, eine andere Methode als die vom OP beschriebene Methode minimal zu veranschaulichen.

    – nmichaels

    31. Dezember 2010 um 22:45 Uhr


  • Da beim Casting gegen Null gerundet wird, funktioniert dies nur, wenn x positiv ist.

    – Spleißgerät

    31. Dezember 2010 um 22:46 Uhr

  • Die C-Bibliotheksfunktion round( ) gibt ein Double zurück, kein Int.

    – Stefan Kanon

    31. Dezember 2010 um 23:12 Uhr

  • Und das Argument sollte sein double x nicht float xSchwimmer wären float roundf(float x). Ich denke, Sie werden auch undefiniertes Verhalten erhalten, wenn x ist außerhalb der [INT_MIN,INT_MAX] Intervall.

    – mu ist zu kurz

    1. Januar 2011 um 0:44 Uhr


Benutzer-Avatar
Mu ist zu kurz

IEEE 754 empfiehlt den „Round Half to Even“-Ansatz: Wenn der Bruchteil von d 0,5 ist, dann auf die nächste gerade ganze Zahl runden. Das Problem besteht darin, dass das Runden eines Bruchteils von 0,5 in die gleiche Richtung zu Verzerrungen in den Ergebnissen führt; Sie müssen also einen Bruchteil von 0,5 die Hälfte der Zeit aufrunden und die Hälfte der Zeit abrunden, daher würde das Bit “Auf die nächste gerade Ganzzahl runden”, das Runden auf die nächste Ungerade würde auch funktionieren, als würde man eine faire Münze werfen, um den Weg zu bestimmen gehen.

Ich denke, so etwas wäre IEEE-korrekt:

#include <math.h>

int is_even(double d) {
    double int_part;
    modf(d / 2.0, &int_part);
    return 2.0 * int_part == d;
}

double round_ieee_754(double d) {
    double i = floor(d);
    d -= i;
    if(d < 0.5)
        return i;
    if(d > 0.5)
        return i + 1.0;
    if(is_even(i))
        return i;
    return i + 1.0;
}

Und dieser sollte C99-ish sein (was anscheinend angibt, dass Zahlen mit Nachkommastellen von 0,5 von Null weg gerundet werden sollten):

#include <math.h>
double round_c99(double x) {
    return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
}

Und eine kompaktere Version meiner ersten round_c99()dieser handhabt das Überschreiten der 56-Bit-Mantissengrenze besser, indem er sich nicht darauf verlässt x+0.5 oder x-0.5 sinnvolle Dinge zu tun:

#include <math.h>
double round_c99(double d) {
    double int_part, frac_part;
    frac_part = modf(d, &int_part);
    if(fabs(frac_part) < 0.5)
        return int_part;
    return int_part > 0.0 ? int_part + 1.0 : int_part - 1.0;
}

Dies wird Probleme haben, wenn |int_part| >> 1 aber ein Double mit einem großen Exponenten zu runden ist sinnlos. Ich bin mir sicher, dass in allen dreien auch NaN stecken, aber mein Masochismus hat Grenzen und numerisches Programmieren ist wirklich nicht mein Ding.

Die Gleitkommaberechnung bietet also ausreichend Platz für subtile Fehler prägnant vielleicht nicht die beste Voraussetzung.

Eine noch bessere Lösung wäre, Ihren Compiler-Anbieter grob ins Gesicht zu schlagen, bis er eine richtige Mathematikbibliothek bereitstellt.

In früheren Zeiten, als das Runden systemübergreifend nicht gut definiert war, schrieben wir eine skalierte Rundungsfunktion, die zuerst die Zahl multiplizierte, sodass die Rundung durch Abschneiden der Zahl erfolgte.
Um auf 2 Dezimalstellen zu runden, multipliziere mit 100, addiere 0,5, kürze die Ergebnisse und dividiere durch 100.
So wurde es bei Werkzeugmaschinen mit numerischer Steuerung gemacht, wenn die Steuerung ein NC-Programm nicht ausführen konnte, es sei denn, es war genau richtig (tote Nüsse).

Benutzer-Avatar
Jim Mcnamara

Eine Möglichkeit, eine Zeichenfolgenoperation zu verwenden

float var=1.2345;
char tmp[12]={0x0};
sprintf(tmp, "%.2f", var);
var=atof(tmp);

Eine andere Möglichkeit, numerische Operationen zu verwenden

float var=1.2345;
int i=0;
var*=100;
i=var;
var=i;
var/=100;

Benutzer-Avatar
sampathris

Kannst du Integer verwenden? Mach Folgendes :

int roundup(int m, int n)
{
 return (m + n - 1) / n  ;
}

int rounddown(int m, int n)
{
 return m / n;
}

int round(int m, int n)
{
   int mod = m % n;

   if(mod >= (n + 1) / 2)
      return roundup(m, n);
   else
      return rounddown(m, n);
}

1030980cookie-checkPrägnante Möglichkeit, round() in C zu implementieren?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy