Domanda

Ho bisogno di una semplice funzione di arrotondamento in virgola mobile, quindi:

double round(double);

round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1

posso trovare ceil() E floor() in math.h - ma no round().

È presente nella libreria standard C++ sotto un altro nome o manca??

È stato utile?

Soluzione

Non c'è round () nella libreria standard C ++ 98. Puoi scriverne uno tu però. Quella che segue è un'implementazione di round-half-up :

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

Il probabile motivo per cui non esiste una funzione rotonda nella libreria standard C ++ 98 è che può effettivamente essere implementato in diversi modi. Quanto sopra è un modo comune ma ce ne sono altri come round-to-even , che è meno distorto e generalmente migliore se hai intenzione di fare molti arrotondamenti; è un po 'più complesso da implementare.

Altri suggerimenti

Boost offre un semplice set di funzioni di arrotondamento.

#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

Per ulteriori informazioni, vedere il Migliora la documentazione.

Modificare:Dal C++11, ci sono std::round, std::lround, E std::llround.

Lo standard C ++ 03 si basa sullo standard C90 per quello che lo standard chiama Standard C Library che è coperto nella bozza dello standard C ++ 03 ( lo standard di bozza più vicino pubblicamente disponibile a C ++ 03 è N1804 ) sezione 1.2 Riferimenti normativi :

  

La biblioteca descritta nella clausola 7 della ISO / IEC 9899: 1990 e nella clausola 7 della   ISO / IEC 9899 / Amd.1: 1995 è di seguito chiamato Standard C   Biblioteca. 1)

Se andiamo alla documentazione C per round, lround, llround su cppreference possiamo vedere che round e le relative funzioni fanno parte di C99 e quindi non saranno disponibili in C ++ 03 o precedenti.

In C ++ 11 questo cambia poiché C ++ 11 si basa sulla bozza di standard C99 per libreria standard C e quindi fornisce std :: round e per i tipi di ritorno integrale 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 ;
}

Un'altra opzione anche da C99 sarebbe std :: trunc che :

  

Calcola il numero intero più vicino non più grande dell'arg.

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

}

Se devi supportare applicazioni non C ++ 11, la soluzione migliore sarebbe usare boost round, iround, lround, llround o boost trunc .

Rotolare la tua versione di round è difficile

Probabilmente non vale la pena fare il roll-up come Più difficile di quanto sembri: arrotondare float all'intero più vicino, parte 1 , Arrotondamento del float al numero intero più vicino, parte 2 e Arrotondamento del float al numero intero più vicino, parte 3 spiegare:

Ad esempio, un roll comune della tua implementazione utilizzando std::floor e l'aggiunta di 0.5 non funziona per tutti gli input:

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

Un input per cui questo fallirà è 0.49999999999999994, ( vederlo dal vivo ).

Un'altra implementazione comune prevede il cast di un tipo a virgola mobile in un tipo integrale, che può invocare un comportamento indefinito nel caso in cui la parte integrale non possa essere rappresentata nel tipo di destinazione. Possiamo vederlo dalla bozza della sezione standard C ++ 4.9 Conversioni a virgola mobile integrale che dice ( enfasi mia ):

  

Un valore di un tipo a virgola mobile può essere convertito in un valore di un   tipo intero. La conversione tronca; cioè la parte frazionaria   viene scartato. Il comportamento non è definito se il valore troncato non può   essere rappresentato nel tipo di destinazione. [...]

Ad esempio:

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

Dato std::numeric_limits<unsigned int>::max() è 4294967295 quindi la seguente chiamata:

myround( 4294967296.5f ) 

causerà overflow, ( vederlo dal vivo ) .

Possiamo vedere quanto sia davvero difficile guardando questa risposta a Modo conciso per implementare round () in C? che fa riferimento alla versione newlibs della singola virgola mobile di precisione circolare. È una funzione molto lunga per qualcosa che sembra semplice. Sembra improbabile che chiunque non abbia una conoscenza intima dell'implant a virgola mobileLe istruzioni potrebbero implementare correttamente questa funzione:

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

D'altra parte se nessuna delle altre soluzioni è utilizzabile newlib potrebbe potenzialmente essere un'opzione poiché è un'implementazione ben collaudata.

Potrebbe valere la pena notare che se si desidera un risultato intero dall'arrotondamento, non è necessario passarlo attraverso il soffitto o il pavimento. Cioè.,

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

È disponibile da C ++ 11 in cmath (secondo 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;
}

Output:

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

Di solito è implementato come floor(value + 0.5).

Modifica: e probabilmente non è chiamato round poiché ci sono almeno tre algoritmi di arrotondamento che conosco: round a zero, round al numero intero più vicino e arrotondamento del banco. Stai chiedendo il numero intero tondo o più vicino.

Ci sono 2 problemi che stiamo esaminando:

  1. arrotondamento delle conversioni
  2. conversione del tipo.

Le conversioni di arrotondamento significano arrotondamento & # 177; galleggiante / doppio al piano più vicino / soffitto galleggiante / doppio. Potrebbe essere il tuo problema finisce qui. Ma se si prevede di restituire Int / Long, è necessario eseguire la conversione del tipo, quindi & Quot; Overflow & Quot; il problema potrebbe colpire la tua soluzione. Quindi, verifica la presenza di errori nella tua funzione

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))

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

Un certo tipo di arrotondamento è implementato anche in Boost:

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

Nota che funziona solo se esegui una conversione in intero.

Puoi arrotondare ad una precisione di n cifre con:

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

Se alla fine si desidera convertire l'output double della funzione round() in int, le soluzioni accettate di questa domanda saranno simili a:

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

Questo si aggancia intorno a 8,88 & nbsp; ns sulla mia macchina quando viene passato in valori uniformemente casuali.

Quanto segue è funzionalmente equivalente, per quanto ne so, ma si collega a 2.48 & nbsp; ns sulla mia macchina, per un significativo vantaggio in termini di prestazioni:

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

Tra i motivi della migliore prestazione c'è la diramazione saltata.

Al giorno d'oggi non dovrebbe essere un problema utilizzare un compilatore C++11 che include una libreria matematica C99/C++11.Ma allora la domanda diventa:quale funzione di arrotondamento scegli?

C99/C++11 round() spesso non è effettivamente la funzione di arrotondamento desiderata.Utilizza una modalità di arrotondamento originale che arrotonda da 0 come tie-break nei casi a metà (+-xxx.5000).Se desideri specificamente quella modalità di arrotondamento o stai prendendo di mira un'implementazione C++ where round() è più veloce di rint(), quindi usalo (o emula il suo comportamento con una delle altre risposte a questa domanda che lo ha preso per valore nominale e ha riprodotto attentamente quello specifico comportamento di arrotondamento.)

round()L'arrotondamento è diverso da quello predefinito IEEE754 turno alla modalità più vicina con parità di tie-break.Il valore più vicino alla pari evita la distorsione statistica nella grandezza media dei numeri, ma tende verso i numeri pari.

Esistono due funzioni di arrotondamento della libreria matematica che utilizzano la modalità di arrotondamento predefinita corrente: std::nearbyint() E std::rint(), entrambi aggiunti in C99/C++11, quindi sono disponibili in qualsiasi momento std::round() È.L'unica differenza è questa nearbyint non rilancia mai FE_INEXACT.

Preferire rint() per ragioni di prestazione:gcc e clang sono entrambi inline più facilmente, ma gcc non si inlineerà mai nearbyint() (anche con -ffast-math)


gcc/clang per x86-64 e AArch64

Ne ho messi alcuni funzioni di test sul Compiler Explorer di Matt Godbolt, dove puoi vedere source + output asm (per più compilatori).Per ulteriori informazioni sulla lettura dell'output del compilatore, vedere queste domande e risposte, e il discorso di Matt alla CppCon2017: “Cosa ha fatto per me il mio compilatore ultimamente?Sbloccare il coperchio del compilatore”,

Nel codice FP, di solito è una grande vittoria incorporare piccole funzioni.Soprattutto su sistemi non Windows, dove la convenzione di chiamata standard non prevede registri conservati dalle chiamate, quindi il compilatore non può mantenere alcun valore FP nei registri XMM attraverso un call.Quindi, anche se non conosci veramente asm, puoi comunque vedere facilmente se si tratta solo di una chiamata in coda alla funzione di libreria o se è incorporata in una o due istruzioni matematiche.Tutto ciò che è in linea con una o due istruzioni è migliore di una chiamata di funzione (per questa particolare attività su x86 o ARM).

Su x86, tutto ciò che è in linea con SSE4.1 roundsd può vettorizzare automaticamente con SSE4.1 roundpd (o AVX vroundpd).(Le conversioni FP->intero sono disponibili anche in formato SIMD compresso, ad eccezione di FP->intero a 64 bit che richiede AVX512.)

  • std::nearbyint():

    • suono x86:in linea con un singolo INSN -msse4.1.
    • x86cc:inline a un singolo insn solo con -msse4.1 -ffast-math, e solo su gcc 5.4 e prima.Successivamente gcc non lo inserisce mai (forse non si sono resi conto che uno dei bit immediati può sopprimere l'eccezione inesatta?Questo è ciò che usa clang, ma il vecchio gcc usa lo stesso immediato di for rint quando lo fa in linea)
    • AArch64 gcc6.3:inline a un singolo insn per impostazione predefinita.
  • std::rint:

    • suono x86:in linea con un singolo INSN -msse4.1
    • x86gcc7:in linea con un singolo INSN -msse4.1.(Senza SSE4.1, in linea con diverse istruzioni)
    • x86 gcc6.x e versioni precedenti:in linea con un singolo INSN -ffast-math -msse4.1.
    • AArch64 gcc:inline a un singolo insn per impostazione predefinita
  • std::round:

    • suono x86:non è in linea
    • x86cc:inline a più istruzioni con -ffast-math -msse4.1, richiedendo due costanti vettoriali.
    • AArch64 gcc:inline a una singola istruzione (supporto HW per questa modalità di arrotondamento, nonché l'impostazione predefinita IEEE e la maggior parte delle altre).
  • std::floor / std::ceil / std::trunc

    • suono x86:in linea con un singolo INSN -msse4.1
    • x86 gcc7.x:in linea con un singolo INSN -msse4.1
    • x86 gcc6.x e versioni precedenti:in linea con un singolo INSN -ffast-math -msse4.1
    • AArch64 gcc:inline per impostazione predefinita su una singola istruzione

Arrotondando a int / long / long long:

Hai due opzioni qui:utilizzo lrint (Piace rint ma ritorna long, O long long per llrint), oppure utilizzare una funzione di arrotondamento FP->FP e quindi convertire in un tipo intero nel modo normale (con troncamento).Alcuni compilatori ottimizzano in un modo meglio dell'altro.

long l = lrint(x);

int  i = (int)rint(x);

Notare che int i = lrint(x) converte float O double -> long prima, quindi tronca il numero intero a int.Questo fa la differenza per gli interi fuori intervallo:Comportamento non definito in C++, ma ben definito per le istruzioni x86 FP -> int (che il compilatore emetterà a meno che non veda l'UB in fase di compilazione durante la propagazione costante, quindi è consentito creare codice che si interrompe se mai viene eseguito).

Su x86, viene prodotta una conversione FP->intero che supera l'intero INT_MIN O LLONG_MIN (un piccolo schema di 0x8000000 o l'equivalente a 64 bit, con solo il bit di segno impostato).Intel lo chiama valore "intero indefinito".(Vedere IL cvttsd2si inserimento manuale, l'istruzione SSE2 che converte (con troncamento) il doppio scalare in un intero con segno.È disponibile con destinazione numeri interi a 32 o 64 bit (solo in modalità a 64 bit).C'è anche un cvtsd2si (convertire con la modalità di arrotondamento corrente), che è ciò che vorremmo che il compilatore emettesse, ma sfortunatamente gcc e clang non lo faranno senza -ffast-math.

Fai attenzione anche a quel FP da/per unsigned int / long è meno efficiente su x86 (senza AVX512).La conversione a 32 bit senza segno su una macchina a 64 bit è piuttosto economica;basta convertirlo in 64 bit firmato e troncare.Ma per il resto è significativamente più lento.

  • x86 clangore con/senza -ffast-math -msse4.1: (int/long)rint in linea con roundsd / cvttsd2si.(ottimizzazione mancata a cvtsd2si). lrint non è affatto in linea.

  • x86 gcc6.x e versioni precedenti senza -ffast-math:in nessun caso in linea

  • x86 gcc7 senza -ffast-math: (int/long)rint arrotonda e converte separatamente (con 2 istruzioni totali di SSE4.1 è abilitato, altrimenti con un mucchio di codice incorporato per rint senza roundsd). lrint non è in linea.
  • x86cc con -ffast-math: tutti i modi in linea con cvtsd2si (ottimale), non è necessario SSE4.1.

  • AArch64 gcc6.3 senza -ffast-math: (int/long)rint in linea con 2 istruzioni. lrint non è in linea

  • AArch64 gcc6.3 con -ffast-math: (int/long)rint compila in una chiamata a lrint. lrint non è in linea.Questa potrebbe essere un'ottimizzazione mancata a meno che non si ottengano le due istruzioni senza -ffast-math sono molto lenti.

Attenzione a floor(x+0.5). Ecco cosa può accadere per i numeri dispari nell'intervallo [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

Questo è http://bugs.squeak.org/view.php?id = 7134 . Usa una soluzione come quella di @konik.

La mia versione robusta sarebbe simile a:

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

Un altro motivo per evitare il piano (x + 0,5) è dato qui .

Non è necessario implementare nulla, quindi non sono sicuro del motivo per cui così tante risposte implicano definizioni, funzioni o metodi.

In C99

Abbiamo i seguenti ee header < tgmath.h > per macro di tipo generico.

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

Se non puoi compilarlo, probabilmente hai lasciato fuori la libreria matematica. Un comando simile a questo funziona su ogni compilatore C che ho (diversi).

gcc -lm -std=c99 ...

In C ++ 11

Abbiamo i seguenti e ulteriori sovraccarichi in #include < cmath > che si basano su virgola mobile IEEE a precisione doppia.

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

Esistono equivalenti nello spazio dei nomi standard .

Se non puoi compilarlo, potresti usare la compilazione C invece di C ++. Il seguente comando di base non produce né errori né avvisi con g ++ 6.3.1, x86_64-w64-mingw32-g ++ 6.3.0, clang-x86_64 ++ 3.8.0 e Visual C ++ 2015 Community.

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

Con divisione ordinale

Quando si dividono due numeri ordinali, dove T è breve, int, lungo o un altro ordinale, l'espressione di arrotondamento è questa.

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

Precisione

Non vi è dubbio che inesattezze a virgola mobile compaiono inesattezze dall'aspetto strano, ma questo è solo quando compaiono i numeri e ha poco a che fare con l'arrotondamento.

La fonte non è solo il numero di cifre significative nella mantissa della rappresentazione IEEE di un numero in virgola mobile, è correlato al nostro pensiero decimale come umani.

Dieci è il prodotto di cinque e due e 5 e 2 sono relativamente primi. Pertanto, gli standard IEEE in virgola mobile non possono essere rappresentati perfettamente come numeri decimali per tutte le rappresentazioni digitali binarie.

Questo non è un problema con gli algoritmi di arrotondamento. È la realtà matematica che dovrebbe essere considerata durante la selezione dei tipi e la progettazione di calcoli, immissione dei dati e visualizzazione dei numeri. Se un'applicazione visualizza le cifre che mostrano questi problemi di conversione decimale-binaria, l'applicazione esprime visivamente l'accuratezza che non esiste nella realtà digitale e dovrebbe essere modificata.

Funzione double round(double) con l'uso della modf funzione:

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

Per essere compilato pulito, include " math.h " e " limiti " sono necessari. La funzione funziona secondo uno schema di arrotondamento seguente:

  • il round 5.0 è 5.0
  • il giro di 3.8 è 4.0
  • round di 2.3 è 2.0
  • il round di 1.5 è 2.0
  • round di 0.501 è 1.0
  • il giro di 0.5 è 1.0
  • round di 0.499 è 0.0
  • il giro di 0.01 è 0.0
  • round di 0.0 è 0.0
  • il round di -0,01 è -0,0
  • il giro di -0.499 è -0.0
  • il giro di -0.5 è -0.0
  • il giro di -0.501 è -1.0
  • il giro di -1.5 è -1.0
  • il giro di -2.3 è -2.0
  • il giro di -3,8 è -4,0
  • il giro di -5,0 è -5,0

Se devi essere in grado di compilare il codice in ambienti che supportano lo standard C ++ 11, ma devi anche essere in grado di compilare lo stesso codice in ambienti che non lo supportano, puoi usare una macro di funzione per scegliere tra std :: round () e una funzione personalizzata per ciascun sistema. Basta passare -DCPP11 o /DCPP11 al compilatore conforme a C ++ 11 (o utilizzare le macro della versione integrata) e creare un'intestazione come questa:

// 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 */

Per un rapido esempio, vedi http://ideone.com/zal709 .

Questo si avvicina a std :: round () in ambienti che non sono conformi a C ++ 11, inclusa la conservazione del bit di segno per -0.0. Tuttavia, può causare un leggero calo delle prestazioni e probabilmente avrà problemi con l'arrotondamento di alcuni & Quot noti; problema & Quot; valori in virgola mobile come 0.49999999999999994 o valori simili.

In alternativa, se hai accesso a un compilatore conforme a C ++ 11, puoi semplicemente prendere std :: round () dalla sua intestazione <cmath> e usarlo per creare la tua intestazione che definisce la funzione se è non già definito. Si noti che questa potrebbe non essere una soluzione ottimale, tuttavia, soprattutto se è necessario compilare per più piattaforme.

In base alla risposta di Kalaxy, la seguente è una soluzione basata su modelli che arrotonda qualsiasi numero in virgola mobile al tipo intero più vicino in base all'arrotondamento naturale. Inoltre, genera un errore in modalità debug se il valore è al di fuori dell'intervallo del tipo intero, fungendo quindi da funzione di libreria praticabile.

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

Come sottolineato nei commenti e in altre risposte, la libreria standard ISO C++ non è stata aggiunta round() fino a ISO C++11, quando questa funzione è stata inserita facendo riferimento alla libreria matematica standard ISO C99.

Per operandi positivi in ​​[½, ub] round(x) == floor (x + 0.5), Dove ub è 223 per float quando mappato su IEEE-754 (2008) binary32, e 252 per double quando è mappato su IEEE-754 (2008) binary64.I numeri 23 e 52 corrispondono al numero di immagazzinato bit di mantissa in questi due formati a virgola mobile.Per operandi positivi in ​​[+0, ½) round(x) == 0, e per operandi positivi in ​​(ub, +∞] round(x) == x.Poiché la funzione è simmetrica rispetto all'asse x, argomenti negativi x può essere gestito secondo round(-x) == -round(x).

Questo porta al codice compatto seguente.Si compila in un numero ragionevole di istruzioni macchina su varie piattaforme.Ho osservato il codice più compatto sulle GPU, dove my_roundf() richiede circa una dozzina di istruzioni.A seconda dell'architettura del processore e della toolchain, questo approccio basato su virgola mobile potrebbe essere più veloce o più lento dell'implementazione basata su numeri interi di newlib a cui si fa riferimento in un risposta diversa.

Ho provato my_roundf() esaustivamente contro newlib roundf() implementazione utilizzando il compilatore Intel versione 13, con entrambi /fp:strict E /fp:fast.Ho anche controllato che la versione newlib corrisponda a quella roundf() nel mathimf libreria del compilatore Intel.Non è possibile eseguire test esaustivi per la doppia precisione round(), tuttavia il codice è strutturalmente identico all'implementazione a precisione singola.

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

Uso la seguente implementazione di round in asm per l'architettura x86 e il C ++ specifico per MS VS:

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

UPD: per restituire un doppio valore

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

Output:

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

Il modo migliore per arrotondare un valore variabile di " n " decimali, è il seguente con in O (1) tempo: -

Dobbiamo arrotondare il valore di 3 posizioni, ovvero 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");

Potrebbe essere un modo sporco inefficiente di conversione ma diamine, funziona lol. Ed è buono, perché si applica al float reale. Non influisce solo visivamente sull'output.

Ho fatto questo:

#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;
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top