round() pentru float în C++ (Programare, C++, Virgulă Mobilă, Rotunjire)

Roddy a intrebat.

Am nevoie de o funcție simplă de rotunjire în virgulă mobilă, astfel:

double round(double);

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

Pot găsi ceil() și floor() în math.h – dar nu și round().

Este prezentă în biblioteca standard C++ sub un alt nume sau lipsește?

Comentarii

  • Dacă vrei doar să scoți numărul ca număr rotunjit se pare că poți face doar std::cout << std::fixed << std::setprecision(0) << -0.9, de exemplu. –  > Por Frank.
  • 46

  • Protejarea acestui… Utilizatorii noi cu scheme de rotunjire noi și strălucitoare ar trebui să citească mai întâi răspunsurile existente. –  > Por Shog9.
  • round este disponibil din C++11 în <cmath>. Din păcate, dacă sunteți în Microsoft Visual Studio, încă lipsește: connect.microsoft.com/VisualStudio/feedback/details/775474/… –  > Por Alessandro Jacopson.
  • După cum am notat în răspunsul meu, rularea propriului dvs. round are o mulțime de avertismente. Înainte de C++11, standardul se baza pe C90, care nu includea round. C++11 se bazează pe C99, care are round dar, după cum am menționat, include și trunc care are proprietăți diferite și poate fi mai adecvat în funcție de aplicație. De asemenea, majoritatea răspunsurilor par să ignore faptul că un utilizator poate dori să returneze un tip integral, ceea ce ridică și mai multe probleme. –  > Por Shafik Yaghmour.
  • @uvts_cvs acest lucru nu pare să fie o problemă cu cea mai recentă versiune de Visual Studio, vedeți-o în direct. –  > Por Shafik Yaghmour.
22 răspunsuri
schibum

Este disponibil de la C++11 în cmath (în conformitate cu 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;
}

Ieșire:

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

Comentarii

  • există, de asemenea, și lround și llround pentru rezultate integrale –  > Por sp2danny.
  • @sp2danny: sau mai bine, lrint pentru a utiliza modul de rotunjire curent în loc de round‘s funky departe de zero tiebreak. –  > Por Peter Cordes.
  • Dar cum este legat? –  > Por eri0o.
Andreas Magnusson

Nu există round() în biblioteca standard C++98. Totuși, puteți scrie unul singur. Ceea ce urmează este o implementare a lui round-half-up:

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

Motivul probabil pentru care nu există o funcție round în biblioteca standard C++98 este că aceasta poate fi de fapt implementată în moduri diferite. Cea de mai sus este o modalitate obișnuită, dar există și altele, cum ar fi round-to-even, care este mai puțin tendențios și, în general, mai bun dacă aveți de gând să faceți multe rotunjimi; este totuși puțin mai complex de implementat.

Comentarii

    53

  • Acest lucru nu gestionează corect numerele negative. Răspunsul dat de litb este corect. –  > Por Utilizator înregistrat.
  • 39

  • @InnerJoin: Da, tratează numerele negative în mod diferit față de răspunsul lui litb, dar asta nu îl face „incorect”. –  > Por Roddy.
  • 39

  • Adăugarea a 0,5 înainte de trunchiere nu reușește să rotunjească la cel mai apropiat număr întreg pentru mai multe intrări, inclusiv 0,49999999999999999999994. A se vedea blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1 –  > Por Pascal Cuoq.
  • @Sergi0: Nu există „corect” și „incorect” pentru că există mai multe definiții ale rotunjirii care decid ce se întâmplă la jumătatea distanței. Verificați faptele înainte de a judeca. –  > Por Jon.
  • 17

  • @MuhammadAnnaqeeb: Aveți dreptate, lucrurile s-au îmbunătățit enorm de la lansarea C++11. Această întrebare a fost pusă și a primit răspuns într-o altă perioadă în care viața era grea și bucuriile erau puține. Rămâne aici ca o odă adusă eroilor care au trăit și au luptat atunci și pentru acele biete suflete care încă nu sunt în stare să folosească instrumentele moderne. –  > Por Andreas Magnusson.
Daniel Wolf

Boost oferă un set simplu de funcții de rotunjire.

#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

Pentru mai multe informații, consultați pagina documentația Boost.

Editați: Începând cu C++11, există std::round, std::lround, și std::llround.

Comentarii

  • Eu foloseam deja boost în proiectul meu, +1 pentru asta, mult mai bine decât să folosesc naivul floor(value + 0.5) abordare naivă! –  > Por Gustavo Maciel.
  • @GustavoMaciel Știu că am întârziat puțin la joc, dar implementarea boost este floor(value + 0.5). –  > Por n. ‘pronouns’ m..
  • De fapt, nu o face: github.com/boostorg/math/blob/develop/include/boost/math/… 4 ani mai târziu, aș vrea să mai spun că floor(value + 0.5) nu este deloc naivă, ci mai degrabă depinde de contextul și natura valorilor pe care doriți să le rotunjiți! –  > Por Gustavo Maciel.
Shafik Yaghmour

Standardul C++03 se bazează pe standardul C90 pentru ceea ce standardul numește Biblioteca C standard care este inclusă în proiectul de standard C++03 (cel mai apropiat proiect de standard disponibil public față de C++03 este N1804.) secțiunea 1.2 Referințe normative:

Biblioteca descrisă în clauza 7 din ISO/CEI 9899:1990 și în clauza 7 din ISO/CEI 9899/Amd.1:1995 este denumită în continuare Biblioteca C standard.1)

Dacă mergem la secțiunea documentația C pentru round, lround, llround pe cppreference putem vedea că round și funcțiile aferente fac parte din C99 și, prin urmare, nu vor fi disponibile în C++03 sau anterior.

În C++11 acest lucru se schimbă, deoarece C++11 se bazează pe proiectul de standard C99 pentru Biblioteca standard C și, prin urmare, oferă std::round, iar pentru tipurile integrale de retur 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 ;
}

O altă opțiune, tot din C99, ar fi std::trunc care:

Calculează cel mai apropiat număr întreg care nu este mai mare în mărime decât 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 ;

}

Dacă aveți nevoie de suport pentru aplicații care nu sunt C++11, cel mai bine ar fi să utilizați boost round, iround, lround, llround, llround sau boost trunc.

Este greu să vă faceți propria versiune de round

Probabil că nu merită efortul de a vă crea propria versiune, deoarece Mai greu decât pare: rotunjirea flotului la cel mai apropiat număr întreg, partea 1, Rotunjirea flotului la cel mai apropiat număr întreg, partea 2 și Rotunjirea float la cel mai apropiat întreg, partea 3 explicați:

De exemplu, o rotire obișnuită, implementarea dvs. folosind std::floor și adăugarea 0.5 nu funcționează pentru toate intrările:

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

O intrare pentru care acest lucru va eșua este 0.49999999999999994, (a se vedea în direct).

O altă implementare obișnuită implică turnarea unui tip cu virgulă mobilă într-un tip integral, care poate invoca un comportament nedefinit în cazul în care partea integrală nu poate fi reprezentată în tipul de destinație. Putem vedea acest lucru din secțiunea din proiectul de standard C++ 4.9 Conversii între virgulă flotantă și integrală care spune (sublinierea îmi aparține):

O valoare de tip virgulă mobilă poate fi convertită într-o valoare de tip întreg. Conversia trunchiază; adică partea fracționară este eliminată. Comportamentul este nedefinit în cazul în care valoarea trunchiată nu poate fi reprezentată în tipul de destinație.[…]

De exemplu:

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

Având în vedere std::numeric_limits<unsigned int>::max() este 4294967295 atunci următorul apel:

myround( 4294967296.5f ) 

va provoca o depășire, (a se vedea în direct).

Putem vedea cât de dificil este cu adevărat acest lucru uitându-ne la acest răspuns la Concise way to implement round() in C? care face referire la newlibs versiunea de rotunjire a floaturilor cu o singură precizie. Este o funcție foarte lungă pentru ceva ce pare simplu. Pare puțin probabil ca cineva care nu are cunoștințe profunde despre implementările în virgulă mobilă să poată implementa corect această funcție:

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

Pe de altă parte, dacă niciuna dintre celelalte soluții nu este utilizabilă newlib ar putea fi o opțiune, deoarece este o implementare bine testată.

Comentarii

  • @downvoter vă rog să explicați ce poate fi îmbunătățit? Marea majoritate a răspunsurilor de aici sunt pur și simplu greșite, deoarece încearcă să își rostogolească propria rundă, care toate eșuează într-o formă sau alta. Dacă lipsește ceva în explicația mea, vă rog să mă anunțați. –  > Por Shafik Yaghmour.
  • Frumos răspuns complet – în special partea cu puțin sub 0,5. O altă nișă: round(-0.0). C spec nu pare să specifice. M-aș aștepta ca -0.0 ca rezultat. –  > Por chux – Reinstaurați-o pe Monica.
  • @chux interesant, iar standardul IEEE 754-2008 specifică faptul că rotunjirea păstrează semnele zerourilor și ale infiniturilor (vezi 5.9). –  > Por Ruslan.
  • @Shafik acesta este un răspuns excelent. Nu m-am gândit niciodată că și rotunjirea este o operație non-trivială. –  > Por Ruslan.
  • Poate că merită menționat faptul că std::rint() este deseori preferabilă lui std::round() atunci când este disponibil C++11 din motive numerice și de performanță. Acesta utilizează modul de rotunjire curent, spre deosebire de round()‘s special mode. Poate fi mult mai eficient pe x86, unde rint se poate alinia la o singură instrucțiune. (gcc și clang fac acest lucru chiar și fără -ffast-math godbolt.org/g/5UsL2e, în timp ce doar clang inlinează aproape echivalentul nearbyint()) ARM are suport pentru o singură instrucțiune pentru round(), dar pe x86 se poate face inline numai cu mai multe instrucțiuni și numai cu -ffast-math –  > Por Peter Cordes.
kalaxy

Ar putea fi demn de remarcat faptul că, dacă ați dorit un rezultat întreg de la rotunjire, nu trebuie să îl treceți nici prin ceil, nici prin floor. Adică,

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

Comentarii

  • Totuși, nu dă rezultatul așteptat pentru 0,499999999999999999999999994 (bineînțeles, depinde de ceea ce aștepți, dar 0 mi se pare mai rezonabil decât 1) –  > Por stijn.
  • @stijn Bună observație. Am constatat că adăugarea sufixului long double literal la constantele mele a rezolvat problema din exemplul dvs., dar nu știu dacă există și alte exemple de precizie pe care nu le-ar fi detectat. –  > Por kalaxy.
  • btw dacă adaugi 0.4999999999999999999999994 în loc de 0.5, funcționează ok atât pentru 0.49999999999999999999994 cât și pentru 5000000000000001.0 ca intrare. Nu sunt sigur dacă este ok pentru toate valorile totuși, și nu am putut găsi nicio referință care să precizeze că aceasta este soluția finală. –  > Por stijn.
  • @stijn Este în regulă pentru toate valorile, dacă nu vă pasă în ce direcție sunt rotunjite valorile aflate exact între două numere întregi. Fără să stau pe gânduri, aș dovedi-o prin analiză de caz cu următoarele cazuri: 0 <= d < 0,5, 0,5 <= d < 1,5, 1,5 <= d < 2^52, d >= 2^52. De asemenea, am testat exhaustiv cazul cu o singură precizie. –  > Por Pascal Cuoq.
  • Per 4.9 [conv.fpint], „Comportamentul este nedefinit în cazul în care valoarea trunchiată nu poate fi reprezentată în tipul de destinație.”, deci acest lucru este puțin periculos. Alte răspunsuri SO descriu cum se poate face acest lucru în mod robust. –  > Por Tony Delroy.
MSN

De obicei, este implementat ca floor(value + 0.5).

Editare: și probabil că nu se numește rotunjire, deoarece există cel puțin trei algoritmi de rotunjire pe care îi cunosc: rotunjire la zero, rotunjire la cel mai apropiat număr întreg și rotunjirea bancherului. Tu ceri rotunjirea la cel mai apropiat întreg.

Comentarii

  • Este bine să se facă distincția între diferitele versiuni de „round”. De asemenea, este bine să știi când să alegi care dintre ele. –  > Por xtofl.
  • Într-adevăr, există diferiți algoritmi de rotunjire care pot avea pretenții rezonabile de a fi „corecți”. Cu toate acestea, floor(valoare + 0,5) nu este unul dintre acestea. Pentru unele valori, cum ar fi 0,4999999997f sau echivalentul dublu, răspunsul este pur și simplu greșit – va fi rotunjit la 1,0 când toți sunt de acord că ar trebui să fie zero. Consultați această postare pentru detalii: blog.frama-c.com/index.php?post/2013/05/02/nearbyintf1 –  > Por Bruce Dawson.
Sangeet

Există 2 probleme pe care le analizăm:

  1. conversiile de rotunjire
  2. conversia de tip.

Conversiile de rotunjire înseamnă rotunjirea ± float/double la cel mai apropiat float/double de podea/ceai.Poate că problema dvs. se termină aici.Dar dacă se așteaptă să returnați Int/Long, trebuie să efectuați conversia de tip și, astfel, problema „Overflow” ar putea afecta soluția dvs. Așadar, verificați dacă există o eroare în funcția dvs.

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

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

Comentarii

  • Folosind LONG_MIN-0.5 și LONG_MAX+0.5 introduce complicații, deoarece matematica poate să nu fie exactă. LONG_MAX poate depăși double precizia pentru o conversie exactă. În continuare probabil că doriți assert(x < LONG_MAX+0.5); (< vs <=) ca LONG_MAX+0.5 poate fi reprezentabil cu exactitate și (x)+0.5 poate avea un rezultat exact de LONG_MAX+1 care nu reușește long cast. Și alte probleme de colț. –  > Por chux – Reintroduceți-o pe Monica.
  • Nu-ți apela funcția round(double), există deja o funcție standard a bibliotecii matematice cu acest nume (în C++11), deci este confuz. Folosiți std::lrint(x) dacă este disponibilă. –  > Por Peter Cordes.
Philipp

Un anumit tip de rotunjire este, de asemenea, implementat în 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;
}

Rețineți că acesta funcționează numai dacă efectuați o conversie în numere întregi.

Comentarii

  • Boost oferă, de asemenea, un set de funcții simple de rotunjire; consultați răspunsul meu. –  > Por Daniel Wolf.
  • De asemenea, puteți utiliza boost:numeric::RoundEven< double >::nearbyint direct, dacă nu doriți să folosiți „to-integer”. @DanielWolf rețineți că funcția simplă este implementată folosind +0,5, care are probleme, așa cum a fost expus de aka.nice –  > Por stijn.
Peter Cordes

În aceste zile nu ar trebui să fie o problemă să folosiți un compilator C++11 care include o bibliotecă matematică C99/C++11. Dar atunci se pune întrebarea: ce funcție de rotunjire alegeți?

C99/C++11 round() de multe ori nu este de fapt funcția de rotunjire pe care o doriți. Folosește un mod de rotunjire ciudat care rotunjește la distanță de 0 ca o modalitate de departajare în cazurile la jumătatea drumului (+-xxx.5000). Dacă doriți în mod special acest mod de rotunjire sau dacă vă adresați unei implementări C++ în care round() este mai rapidă decât rint(), atunci folosiți-l (sau emulați-i comportamentul cu unul dintre celelalte răspunsuri la această întrebare, care a luat-o ca atare și a reprodus cu atenție acel comportament specific de rotunjire).

round()rotunjirea lui ‘s este diferită de cea implicită IEEE754. rotunjire la modul cel mai apropiat cu egalitate de puncte ca și mod de departajare. Cea mai apropiată pară evită distorsiunea statistică în ceea ce privește mărimea medie a numerelor, dar favorizează numerele pare.

Există două funcții de rotunjire din biblioteca matematică care utilizează modul de rotunjire implicit actual: std::nearbyint() și std::rint(), ambele adăugate în C99/C++11, deci sunt disponibile oricând. std::round() este. Singura diferență este că nearbyint nu ridică niciodată FE_INEXACT.

Preferați rint() din motive de performanță: atât gcc cât și clang îl inlinează mai ușor, dar gcc nu îl inlinează niciodată nearbyint() (chiar și cu -ffast-math)


gcc/clang pentru x86-64 și AArch64

Am pus niște funcții de test pe Compiler Explorer al lui Matt Godbolt, unde puteți vedea sursa + ieșirea asm (pentru mai multe compilatoare). Pentru mai multe informații despre citirea ieșirii compilatorului, consultați acest Q&A, precum și discuția lui Matt de la CppCon2017: „What Has My Compiler Done for Me Lately? Unbolting the Compiler’s Lid”,

În codul FP, de obicei este un mare câștig să incluzi în linie funcții mici. Mai ales în afara Windows, unde convenția standard de apelare nu are registre conservate pentru apeluri, astfel încât compilatorul nu poate păstra nicio valoare FP în registrele XMM în timpul unui call. Astfel, chiar dacă nu cunoașteți cu adevărat asm, puteți vedea cu ușurință dacă este vorba doar de un apel de coadă la funcția de bibliotecă sau dacă a fost inclusă în linie la una sau două instrucțiuni matematice. Orice lucru care face inline la una sau două instrucțiuni este mai bun decât un apel de funcție (pentru această sarcină specială pe x86 sau ARM).

Pe x86, orice lucru care se încadrează în SSE4.1 roundsd se poate autovectoriza cu SSE4.1 roundpd (sau AVX vroundpd). (Conversiile FP->integer sunt, de asemenea, disponibile în forma SIMD împachetată, cu excepția FP->integer pe 64 de biți, care necesită AVX512).

  • std::nearbyint():

    • x86 clang: inline la un singur insn cu -msse4.1.
    • x86 gcc: inlines la un singur insn numai cu -msse4.1 -ffast-math, și numai pe gcc 5.4 și anterioare. Mai târziu, gcc nu îl inlinează niciodată (poate că nu și-au dat seama că unul dintre biții imediați poate suprima excepția inexactă? Asta este ceea ce folosește clang, dar gcc mai vechi folosește același immediate ca pentru rint atunci când o introduce în linie)
    • AArch64 gcc6.3: inlinează la un singur insn în mod implicit.
  • std::rint:

    • x86 clang: inline la un singur insn cu -msse4.1
    • x86 gcc7: se introduce un singur insn cu -msse4.1. (Fără SSE4.1, inlines la mai multe instrucțiuni)
    • x86 gcc6.x și versiunile anterioare: inlines la un singur insn cu -ffast-math -msse4.1.
    • AArch64 gcc: inline la un singur insn în mod implicit
  • std::round:

    • x86 clang: nu face inline
    • x86 gcc: se face inline la mai multe instrucțiuni cu -ffast-math -msse4.1, care necesită două constante vectoriale.
    • AArch64 gcc: inlines la o singură instrucțiune (suport HW pentru acest mod de rotunjire, precum și pentru modul implicit IEEE și majoritatea celorlalte).
  • std::floor / std::ceil / std::trunc

    • x86 clang: inline la o singură insn cu -msse4.1
    • x86 gcc7.x: inlines la un singur insn cu -msse4.1
    • x86 gcc6.x și versiunile anterioare: inlines la un singur insn cu -ffast-math -msse4.1
    • AArch64 gcc: inlines în mod implicit la o singură instrucțiune

Rotunjirea la int / long / long long:

Aveți două opțiuni aici: utilizați lrint (de exemplu rint dar returnează long, sau long long pentru llrint), sau utilizați o funcție de rotunjire FP->FP și apoi convertiți la un tip întreg în mod normal (cu trunchiere). Unele compilatoare optimizează mai bine un mod decât celălalt.

long l = lrint(x);

int  i = (int)rint(x);

Rețineți că int i = lrint(x) convertește float sau double -> long mai întâi, iar apoi trunchiază numărul întreg la int. Acest lucru face o diferență în cazul numerelor întregi care nu se încadrează în intervalul de valori: Comportament nedefinit în C++, dar bine definit în cazul instrucțiunilor FP -> int din x86 (pe care compilatorul le va emite dacă nu vede UB în momentul compilării în timp ce face propagarea constantelor, iar în acest caz i se permite să creeze cod care se întrerupe dacă este executat vreodată).

Pe x86, o conversie FP->integer care depășește numărul întreg produce INT_MIN sau LLONG_MIN (un model de bit de 0x8000000 sau echivalentul pe 64 de biți, cu doar bitul de semn setat). Intel numește această valoare „număr întreg indefinit”. (A se vedea cvttsd2si intrarea din manual, instrucțiunea SSE2 care convertește (cu trunchiere) dublul scalar în întreg cu semn. Este disponibilă cu destinație de numere întregi pe 32 sau 64 de biți (numai în modul pe 64 de biți). Există, de asemenea, o instrucțiune cvtsd2si (convertește cu modul de rotunjire curent), care este ceea ce am dori ca compilatorul să emită, dar din păcate gcc și clang nu vor face acest lucru fără -ffast-math.

De asemenea, aveți grijă că FP to/from unsigned int / long este mai puțin eficient pe x86 (fără AVX512). Conversia la unsigned pe 32 de biți pe o mașină pe 64 de biți este destul de ieftină; doar convertiți la signed pe 64 de biți și trunchiați. Dar, în rest, este semnificativ mai lent.

  • x86 clang cu/fără -ffast-math -msse4.1: (int/long)rint inlines to roundsd / cvttsd2si. (optimizare ratată pentru cvtsd2si). lrint nu se aliniază deloc.

  • x86 gcc6.x și versiunile anterioare fără -ffast-math: niciunul dintre aceste două moduri nu este inline

  • x86 gcc7 fără -ffast-math: (int/long)rint rotunjește și convertește separat (cu 2 instrucțiuni totale de SSE4.1 este activat, în caz contrar cu o grămadă de cod neliniat pentru rint fără roundsd). lrint nu se aliniază.
  • x86 gcc cu -ffast-math: toate modurile inline la cvtsd2si (optim), nu este nevoie de SSE4.1.

  • AArch64 gcc6.3 fără -ffast-math: (int/long)rint inline la 2 instrucțiuni. lrint nu face inline

  • AArch64 gcc6.3 cu -ffast-math: (int/long)rint compilează la un apel la lrint. lrint nu se aliniază. Aceasta poate fi o optimizare ratată, cu excepția cazului în care cele două instrucțiuni pe care le obținem fără -ffast-math sunt foarte lente.

Comentarii

  • TODO: ICC și MSVC sunt, de asemenea, disponibile pe Godbolt, dar nu m-am uitat la rezultatele lor pentru acest lucru. editări binevenite… De asemenea: ar fi mai util să defalcați mai întâi pe compilator / versiune și apoi pe funcție în cadrul acesteia? Cei mai mulți oameni nu vor schimba compilatoarele în funcție de cât de bine compilează FP->FP sau FP->rotunjirea numerelor întregi. –  > Por Peter Cordes.
  • +1 pentru recomandare rint() în cazul în care aceasta este o alegere fezabilă, ceea ce este de obicei cazul. Cred că numele round() implică pentru unii programatori că asta este ceea ce își doresc, în timp ce rint() pare misterios. Rețineți că round() nu utilizează un mod de rotunjire „ciudat”: round-to-nearest-ties-away este un mod oficial de rotunjire IEEE-754 (2008). Este curios faptul că nearbyint() nu este inclusă, având în vedere că este în mare parte la fel ca și rint()și ar trebui să fie identic la -ffast-math condiții. Mie mi se pare că este o eroare. –  > Por njuffa.
Carl

Ai putea rotunji la o precizie de n cifre cu:

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

Comentarii

  • Cu excepția cazului în care dimensiunea int a compilatorului tău nu este implicită la 1024 de biți, acest lucru nu va fi precis pentru un dublu uriaș… –  > Por aka.nice.
  • Cred că este acceptabil având în vedere când va fi folosit: Dacă valoarea ta dublă este 1.0 e+19, rotunjirea la 3 locuri nu are sens. –  > Por Carl.
  • sigur, dar întrebarea este pentru o rotunjire generică și nu poți controla modul în care va fi folosită. Nu există niciun motiv pentru ca rotunjirea să eșueze acolo unde nu ar eșua plafonul și podeaua. –  > Por aka.nice.
  • Acest lucru are un comportament nedefinit pentru argetele din afara domeniului de acțiune al lui int. (În practică, pe x86, valorile FP în afara intervalului FP va face ca CVTTSD2SI să producă 0x80000000 ca model de biți întregi, adică. INT_MIN, care vor fi apoi convertite înapoi în double. –  > Por Peter Cordes.
aka.nice

Feriți-vă de floor(x+0.5). Iată ce se poate întâmpla pentru numerele impare din intervalul [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
",x);
    printf("round(x)    =%f
",y);
    printf("floor(x+0.5)=%f
",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

Aceasta este http://bugs.squeak.org/view.php?id=7134. Folosiți o soluție precum cea a lui @konik.

Propria mea versiune robustă ar fi ceva de genul:

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

Un alt motiv pentru a evita floor(x+0,5) este dat aici.

Comentarii

  • Mă interesează să aflu despre downvotes. Este din cauză că egalitatea este rezolvată mai degrabă departe de zero decât la cel mai apropiat par? –  > Por aka.nice.
  • Notă: specificația C spune că „rotunjirea cazurilor la jumătatea distanței de la zero, indiferent de direcția curentă de rotunjire.”, deci rotunjirea fără a ține cont de impar/par este conformă. –  > Por chux – Reintroduceți-o pe Monica.
dshin

Dacă în cele din urmă doriți să convertiți double ieșirea din round() într-o funcție int, atunci soluțiile acceptate la această întrebare vor arăta ceva de genul:

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

Acest lucru se calculează în jurul valorii de 8,88 ns pe mașina mea atunci când sunt trecute valori uniform aleatoare.

Ceea ce urmează este echivalent din punct de vedere funcțional, din câte îmi dau seama, dar are o valoare de 2,48 ns pe mașina mea, ceea ce reprezintă un avantaj semnificativ în ceea ce privește performanța:

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

Printre motivele pentru performanța mai bună se numără și săriturile de ramificare.

Comentarii

  • Aceasta are un comportament nedefinit pentru argetele din afara intervalului de int. (În practică, pe x86, valorile FP în afara intervalului FP va face ca CVTTSD2SI să producă 0x80000000 ca model de biți întregi, adică. INT_MIN, care vor fi apoi convertite înapoi în double. –  > Por Peter Cordes.
Douglas Daseeco

Nu este nevoie să implementați nimic, așa că nu sunt sigur de ce atât de multe răspunsuri implică definiții, funcții sau metode.

În C99

avem următorul și și și antetul <tgmath.h> pentru macrourile tip-generice.

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

Dacă nu puteți compila acest lucru, probabil că ați omis biblioteca matematică. O comandă similară cu aceasta funcționează pe fiecare compilator C pe care îl am (mai multe).

gcc -lm -std=c99 ...

În C++11

Avem următoarele și supraîncărcări suplimentare în #include <cmath> care se bazează pe virgulă mobilă de precizie dublă IEEE.

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

Există echivalente în spațiul de nume std de asemenea.

Dacă nu puteți compila acest lucru, este posibil să folosiți compilarea C în loc de C++. Următoarea comandă de bază nu produce nici erori, nici avertismente cu g++ 6.3.1, x86_64-w64-mingw32-g++ 6.3.0, clang-x86_64++ 3.8.0 și Visual C++ 2015 Community.

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

Cu diviziunea ordinală

La împărțirea a două numere ordinale, unde T este short, int, long sau un alt ordinal, expresia de rotunjire este următoarea.

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

Precizie

Nu există nicio îndoială că în operațiile cu virgulă mobilă apar inexactități cu aspect ciudat, dar acest lucru se întâmplă numai atunci când apar numerele și are puțin de-a face cu rotunjirea.

Sursa nu este doar numărul de cifre semnificative din mantisa reprezentării IEEE a unui număr în virgulă mobilă, ci este legată de gândirea noastră ca oameni, cea zecimală.

Zece este produsul dintre cinci și doi, iar 5 și 2 sunt relativ prime. Prin urmare, standardele IEEE cu virgulă mobilă nu pot fi reprezentate perfect ca numere zecimale pentru toate reprezentările digitale binare.

Aceasta nu este o problemă cu algoritmii de rotunjire. Este o realitate matematică de care trebuie să se țină seama în timpul selecției tipurilor și al proiectării calculelor, al introducerii datelor și al afișării numerelor. Dacă o aplicație afișează cifrele care prezintă aceste probleme de conversie zecimal-binar, atunci aplicația exprimă vizual o precizie care nu există în realitatea digitală și ar trebui schimbată.

Comentarii

  • „Nu sunt sigur de ce atât de multe răspunsuri implică definiții, funcții sau metode.” Aruncați o privire la momentul în care a fost întrebată – nu ieșise încă C++11. 😉 –  > Por jaggedSpire.
  • @jaggedSpire, păi atunci dă-mi un „thumbs up”, dacă ți se pare potrivit, pentru că toate răspunsurile cu punctaj mare sunt învechite și înșelătoare în contextul celor mai utilizate compilatoare de astăzi. –  > Por Douglas Daseeco.
konik

Funcția double round(double) cu ajutorul funcției modf funcției:

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

Pentru a fi compilat curat, sunt necesare incluziunile „math.h” și „limits”. Funcția funcționează conform următoarei scheme de rotunjire:

  • rotunjirea lui 5,0 este 5,0
  • rotunjire de 3,8 este 4,0
  • rotunjirea de 2,3 este 2,0
  • rotunjire de 1,5 este 2,0
  • rotunjirea de 0,501 este 1,0
  • runda de 0,5 este 1,0
  • runda de 0,499 este 0,0
  • runda de 0.01 este 0.0
  • runda de 0.0 este 0.0
  • runda de -0.01 este -0.0
  • runda de -0,499 este -0,0
  • runda de -0.5 este -0.0
  • runda de -0.501 este -1.0
  • runda de -1.5 este -1.0
  • runda de -2,3 este -2,0
  • runda de -3,8 este -4,0
  • runda de -5.0 este -5.0

Comentarii

  • Aceasta este o soluție bună. Totuși, nu sunt sigur că rotunjirea de la -1,5 la -1,0 este standard, m-aș aștepta la -2,0 prin simetrie. De asemenea, nu văd rostul gărzii de început, primele două dacă ar putea fi eliminate. –  > Por aka.nice.
  • Am verificat în standardul ISO/IEC 10967-2, open-std.org/jtc1/sc22/wg11/docs/n462.pdf și din apendicele B.5.2.4, funcția de rotunjire trebuie într-adevăr să fie simetrică, rounding_F(x) = neg_F(rounding_F(neg_F(x))) – –  > Por aka.nice.
  • Acest lucru va fi lent în comparație cu C++11 rint() sau nearbyint(), dar dacă chiar nu puteți utiliza un compilator care să ofere o funcție de rotunjire adecvată și aveți nevoie de precizie mai mult decât de performanță… –  > Por Peter Cordes.
Justin Time – Reîntregirea Monicăi

Dacă aveți nevoie să puteți compila cod în medii care acceptă standardul C++11, dar trebuie să puteți compila același cod și în medii care nu îl acceptă, ați putea folosi o macro funcție pentru a alege între std::round() și o funcție personalizată pentru fiecare sistem. Trebuie doar să treceți -DCPP11 sau /DCPP11 compilatorului compatibil cu C++11 (sau folosiți macro-urile de versiune încorporate în acesta) și creați un antet ca acesta:

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

Pentru un exemplu rapid, consultați http://ideone.com/zal709 .

Aceasta se apropie de std::round() în mediile care nu sunt compatibile cu C++11, inclusiv păstrarea bitului de semn pentru -0,0. Cu toate acestea, poate cauza o ușoară scădere a performanței și va avea probabil probleme cu rotunjirea anumitor valori cunoscute ca fiind „problematice” în virgulă mobilă, cum ar fi 0,4999999999999999999999994 sau valori similare.

Alternativ, dacă aveți acces la un compilator compatibil cu C++11, ați putea lua std::round() de la pagina sa <cmath> și să îl folosiți pentru a vă crea propriul antet care definește funcția, dacă nu este deja definită. Rețineți însă că este posibil ca această soluție să nu fie optimă, mai ales dacă trebuie să compilați pentru mai multe platforme.

Quant

Pe baza răspunsului lui Kalaxy, următoarea este o soluție modelată care rotunjește orice număr cu virgulă mobilă la cel mai apropiat tip de număr întreg, pe baza rotunjirii naturale. De asemenea, aruncă o eroare în modul de depanare dacă valoarea este în afara intervalului tipului întreg, servind astfel, în linii mari, ca o funcție de bibliotecă viabilă.

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

Comentarii

  • După cum am subliniat în răspunsul meu adăugând 0.5 nu funcționează în toate cazurile. Cu toate că, cel puțin, vă ocupați de problema de depășire a limitei, astfel încât să evitați un comportament nedefinit. –  > Por Shafik Yaghmour.
njuffa

După cum s-a subliniat în comentarii și în alte răspunsuri, biblioteca standard ISO C++ nu a adăugat round() până la ISO C++11, când această funcție a fost introdusă prin trimitere la biblioteca matematică standard ISO C99.

Pentru operanzi pozitivi în [½, ub] round(x) == floor (x + 0.5), unde ub este 223 pentru float atunci când este mapat în conformitate cu IEEE-754 (2008) binary32și 252 pentru double atunci când este mapat cu IEEE-754 (2008) binary64. Numerele 23 și 52 corespund numărului de stocate mantisa în aceste două formate cu virgulă mobilă. Pentru operanzii pozitivi în intervalul [+0, ½) round(x) == 0, și pentru operanzi pozitivi în (ub, +∞] round(x) == x. Deoarece funcția este simetrică în raport cu axa x, argumentele negative x pot fi tratate în conformitate cu round(-x) == -round(x).

Aceasta conduce la codul compact de mai jos. Acesta se compilează într-un număr rezonabil de instrucțiuni mașină pe diferite platforme. Am observat cel mai compact cod pe GPU, unde my_roundf() necesită aproximativ o duzină de instrucțiuni. În funcție de arhitectura procesorului și de lanțul de instrumente, această abordare bazată pe virgulă mobilă ar putea fi mai rapidă sau mai lentă decât implementarea bazată pe numere întregi de la newlib la care se face referire într-un alt răspuns.

Am testat my_roundf() în mod exhaustiv cu newlib roundf() utilizând compilatorul Intel versiunea 13, atât cu /fp:strict și /fp:fast. Am verificat, de asemenea, dacă versiunea newlib se potrivește cu versiunea roundf() din mathimf bibliotecă a compilatorului Intel. Testarea exhaustivă nu este posibilă în cazul calculatoarelor de dublă precizie. round(), însă codul este identic din punct de vedere structural cu cel al implementării cu o singură precizie.

#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
", argi, resi, refi);
            return EXIT_FAILURE;
        }
        argi++;
    } while (argi);
    return EXIT_SUCCESS;
}

Comentarii

  • Am făcut o modificare pentru a evita presupunerea int are o lățime mai mare de 16 biți. Desigur, se presupune în continuare că float este un binary32 IEEE754 de 4 octeți. UN C++11 static_assert sau poate o macro #ifdef / #error ar putea verifica acest lucru. (Dar, desigur, dacă C++11 este disponibil, ar trebui să utilizați std::round, sau pentru modul de rotunjire curent folosiți std::rint care se aliniază frumos cu gcc și clang). –  > Por Peter Cordes.
  • BTW, gcc -ffast-math -msse4.1 inlines std::round() la un add( AND(x, L1), OR(x,L2), și apoi un roundsd. adică implementează destul de eficient round în termeni de rint. Dar nu există niciun motiv pentru a face acest lucru manual în sursa C++, deoarece dacă aveți std::rint() sau std::nearbyint() aveți, de asemenea, și std::round(). Vedeți răspunsul meu pentru un link godbolt și o prezentare a ceea ce se inlinează sau nu cu diferite versiuni gcc/clang. –  > Por Peter Cordes.
  • @PeterCordes Știu foarte bine cum să implementez round() eficient în ceea ce privește rint() (atunci când acesta din urmă funcționează în modul round-to-nearest-or-even): Am implementat acest lucru pentru biblioteca matematică standard CUDA. Cu toate acestea, această întrebare părea să întrebe cum să implementeze round() cu C++ înainte de C++11, așa că rint() nu ar fi nici ea disponibilă, ci doar floor() și ceil(). –  > Por njuffa.
  • @PeterCordes Îmi pare rău, m-am exprimat greșit. round() este ușor de sintetizat din rint() în rotund la zero mod, adică trunc(). N-ar fi trebuit să răspund înainte de prima cafea. –  > Por njuffa.
  • @PeterCordes Sunt de acord că este probabil ca OP să nu aibă nevoie de comportamentul specific de rotunjire al round(); majoritatea programatorilor pur și simplu nu sunt conștienți de distincția dintre round() vs rint() cu rotunjirea la cel mai apropiat egal, în cazul în care acesta din urmă este, de obicei, furnizat direct de hardware și, prin urmare, este mai eficient; am explicat acest lucru în Ghidul de programare CUDA pentru a-i face pe programatori să fie conștienți: „Modul recomandat de rotunjire a unui operand în virgulă mobilă de precizie unică la un număr întreg, rezultatul fiind un număr în virgulă mobilă de precizie unică este rintf(), nu roundf()„. –  > Por njuffa.
Aleksey F.

Folosesc următoarea implementare a rundei în asm pentru arhitectura x86 și MS VS specific C++:

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

UPD: pentru a returna o valoare dublă

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

Ieșire:

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

Comentarii

  • Valoarea rezultată trebuie să fie o valoare în virgulă mobilă cu precizie dublă. –  > Por truthseeker.
  • @ truthseeker: Da, a trebuit să văd tipul cerut de valoare de retur. OK, vezi „UPD”. –  > Por Aleksey F..
  • Compilatorul va intra, sperăm, în linie rint() sau nearbyint() la un SSE4.1 roundsd sau o instrucțiune x87 frndint care va fi mult mai rapidă decât cele două călătorii de stocare/reîncărcare necesare pentru a utiliza acest asm inline pentru datele dintr-un registru. MSVC inline asm este destul de nașpa pentru a înfășura instrucțiuni unice precum frndint deoarece nu există nicio modalitate de a obține intrarea într-un registru. Folosirea acestuia la sfârșitul unei funcții cu rezultatul în st(0) ar putea fi de încredere ca modalitate de returnare a ieșirii; se pare că este sigură pentru eax pentru numere întregi, chiar și atunci când se introduce funcția care conține asm. –  > Por Peter Cordes.
  • @PeterCordes Optimizările moderne sunt binevenite. Cu toate acestea, nu am putut folosi SSE4.1, deoarece nu exista la acel moment. Scopul meu a fost de a oferi o implementare minimă a rundei care să poată funcționa chiar și pe vechile familii Intel P3 sau P4 din anii 2000. –  > Por Aleksey F..
  • P3 nu are nici măcar SSE2, deci compilatorul va folosi deja x87 pentru doubleși, prin urmare, ar trebui să fie capabil să emită frndint singur pentru rint(). În cazul în care compilatorul dvs. utilizează SSE2, săltarea unui double de la un registru XMM la x87 și înapoi s-ar putea să nu merite. –  > Por Peter Cordes.
Parveen Kumar

Cea mai bună metodă de rotunjire a unei valori flotante cu „n” zecimale este următoarea, în timp O(1):-

Trebuie să rotunjim valoarea cu 3 poziții, adică n=3.Deci,

float a=47.8732355;
printf("%.3f",a);

hgrey

Din C++ 11 pur și simplu:

#include <cmath>
std::round(1.1)

sau pentru a obține int

static_cast<int>(std::round(1.1))

Brad
// 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");

S-ar putea să fie un mod ineficient și murdar de conversie, dar la naiba, funcționează lol. Și e bine, pentru că se aplică la float-ul real. Nu doar afectează vizual ieșirea.

Comentarii

  • Aceasta este hilar ineficientă și, de asemenea, trunchiază (eliminând întotdeauna cifrele din urmă) în loc să rotunjească la cea mai apropiată valoare. –  > Por Peter Cordes.
Michael Shum

Eu am făcut asta:

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

Comentarii

  • Nu v-ați referit la pow(10,loc) mai degrabă decât la operatorul binar ^ din 10^loc? 10^2 pe mașina mea îmi dă 8!!! Cu toate acestea pe Mac-ul meu 10.7.4 și gcc, codul nu funcționează, returnând valoarea originală. –  > Por Pete855217.