Am nevoie de o simplă virgulă mobilă rotunjire funcție, astfel:
double round(double);
round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1
Nu pot găsi ceil () " și " floor()
în matematică.h - dar nu round()
.
Este prezent în standardul C++ bibliotecă sub un alt nume, sau este lipsă??
Nu's nu rotunde() în C++98 biblioteca standard. Puteți scrie unul singur totuși. Următoarele este o implementare a rotund-jumătate-up:
double round(double d)
{
return floor(d + 0.5);
}
Motivul probabil nu există nici o runda funcție în C++98 biblioteca standard este că acesta poate fi, de fapt, puse în aplicare în moduri diferite. De mai sus este unul comun, dar există și altele, cum ar fi rotund-pentru-chiar, care este mai puțin părtinitoare și, în general, mai bună dacă ai're de gând să faci o mulțime de rotunjire; it's un pic mai complex pentru a pune în aplicare, deși.
Boost oferă un set simplu de rotunjire funcții.
#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, a se vedea Boost documentation.
Edit: Deoarece C++11, acolo sunt std::rotund
, std::lround", și " std::llround
.
C++03 standard se bazează pe C90 standard pentru ceea ce standard de apeluri C Standard Library care este acoperit în proiecte C++03 standard (mai apropiat dispoziția publicului proiectul de standard C++03 este N1804) secțiunea 1.2
referințe Normative:
biblioteca descrisă în clauza 7 din ISO/IEC 9899:1990 și clauza 7 din ISO/IEC 9899/Amd.1:1995, denumit în continuare Standard C Bibliotecă.1)
Dacă mergem la C documentația pentru runda, lround, llround pe cppreference putem vedea că tur și funcțiile conexe sunt parte a C99 și astfel a câștigat't fi disponibile în C++03 sau înainte.
În C++11 modificări de C++11 se bazează pe C99 proiect de standard pentru biblioteca standard C și, prin urmare, oferă std::rotunde și pentru întoarcere integral tipuri 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, de asemenea, de la C99 ar fi std::trunc care:
Calculeaza cel mai apropiat număr întreg în mărime nu mai mare 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 pentru a sprijini non C++11 aplicații, cel mai bun pariu ar fi să utilizați stimula rotund, iround, lround, llround sau boost trunc.
Rulare propria versiune a rundă este greu
Rulare propriile tale este, probabil, nu merita efortul ca mai Greu decât pare: rotunjire plutesc la cel mai apropiat număr întreg, partea 1, Rotunjire plutesc la cel mai apropiat număr întreg, partea 2 și Rotunjire plutesc la cel mai apropiat număr întreg, partea 3 explica:
De exemplu, o comună rola de implementare folosind std::etaj și adăugarea
0.5` nu funcționează pentru toate intrările:
double myround(double d)
{
return std::floor(d + 0.5);
}
Introducere acest lucru va eșua pentru e 0.49999999999999994
, (vezi live).
Un alt comune de punere în aplicare implică turnarea un punct plutitoare de tip pentru un tip integral, care se poate invoca nedefinit de comportament în cazul în care parte integrantă nu pot fi reprezentați în cadrul tipului de destinație. Putem vedea acest lucru din proiectul de standard C++ secțiunea 4.9` Plutitoare-parte integrantă conversii care spune (sublinierea mea):
Un prvalue de un punct plutitor tip poate fi convertit la un prvalue de o tip întreg. Conversia trunchiază; că este, partea fracționară este aruncată. Comportamentul este nedefinit dacă trunchiat valoare nu poate fie reprezentate în cadrul tipului de destinație.[...]
De exemplu:
float myround(float f)
{
return static_cast<float>( static_cast<unsigned int>( f ) ) ;
}
Dat std::numeric_limits<unsigned int>::max () "este" 4294967295
apoi următorul apel:
myround( 4294967296.5f )
va provoca preaplin, (vezi live).
Putem vedea cât de greu acest lucru este într-adevăr uitandu-se la acest răspuns la mod Concis de a pune în aplicare rotunde() din C? care referențiere newlibs versiune de singur precizie float rotund. Este o foarte lungă perioadă de funcție pentru ceva care pare simplu. Pare puțin probabil ca oricine, fără cunoștințe vaste în virgulă mobilă implementari ar putea să pună corect în aplicare 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ă nici una dintre celelalte soluții sunt utilizabile newlib ar putea fi o opțiune, deoarece este un bine testate în aplicare.
L's disponibil din C++11 în cmath (conform 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
L's, de obicei implementat ca podeaua(valoare + 0.5)`.
Edit: si's, probabil, nu a sunat rotunde, deoarece există cel puțin trei rotunjire algoritmi știu de: rotund la zero, rotunjit la cel mai apropiat număr întreg, și bancherul's rotunjire asupra sumelor. Vi se cere pentru a rotunji la cel mai apropiat număr întreg.
Sunt 2 probleme ne uitam la:
Rotunjirea conversii înseamnă rotunjirea ± float/double la cel mai apropiat de podea/tavan float/double. Poate fi problema ta se termină aici. Dar dacă vă sunt de așteptat să se întoarcă Int/Long, aveți nevoie pentru a efectua conversia de tip, și, astfel, "Preaplin" problema s-ar putea lovi soluție. DECI, face o verificare de eroare în funcție de
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))
Un anumit tip de rotunjire este, de asemenea, puse în aplicare în Impuls:
#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ă acest lucru funcționează doar dacă o faci pentru a-număr întreg de conversie.
Dacă doriți în cele din urmă pentru a converti la "dublu" de ieșire de round()
funcția de a o int
, apoi soluții acceptate de această întrebare va arata ceva de genul:
int roundint(double r) {
return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}
Această ceasuri în jurul valorii de 8.88 ns pe masina mea atunci când a trecut în mod uniform valori aleatoare.
Mai jos este funcțional echivalentă, în măsura în care pot spune, dar ceasuri în la 2.48 ns de pe masina mea, un avantaj semnificativ de performanță:
int roundint (double r) {
int tmp = static_cast<int> (r);
tmp += (r-tmp>=.5) - (r-tmp<=-.5);
return tmp;
}
Printre motivele pentru o performanță mai bună este omisă de ramificare.
Feriți-vă de podea(x+0.5)`. Aici este ceea ce se poate întâmpla pentru numere 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\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
Acest lucru este http://bugs.squeak.org/view.php?id=7134. Utilizați o soluție ca una de @konik.
Propria mea versiune robuste 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 podea(x+0.5) este dat de aici.
runda()
este de multe ori, de fapt, nu rotunjire funcția pe care doriți. Se folosește un funky rotunjire modul runde departe de la 0 ca un tie-break pe jumătate de cazuri (+-xxx.5000
). Dacă o faci în mod special vreau rotunjire modul, sau're vizând o implementare C++ unde round()
este mai rapid decât rint()
, apoi utilizați-l (sau să imite comportamentul său cu una dintre celelalte răspunsuri la această întrebare care a luat-o la valoarea nominală și reproduse cu grijă că de rotunjire specifice de comportament.)
round()
's rotunjire este diferit de IEEE754 default rotunji la cel mai apropiat de modul cu chiar ca un tie-break. Cel mai apropiat-chiar evită statistice părtinire în magnitudine medie de numere, dar nu face părtinire față chiar și numere.
Există două math library rotunjire funcții care folosesc curent implicit de rotunjire modul: std::nearbyint()
și std::rint()
, ambele adăugat în C99/C++11, astfel încât acestea're disponibil în orice moment std::rotunde()
este. Singura diferență este că nearbyint
nu ridică FE_INEXACT.
Prefer rint()
pentru motive de performanță: gcc și zăngănit atât locuri cu mai multă ușurință, dar gcc nu inlines nearbyint()
(chiar și cu -ffast-matematica
) Am pus niste funcții de test de pe Matt Godbolt's Compiler Explorer, în cazul în care puteți vedea sursa + asm ieșire (pentru mai multe compilatoare). Pentru mai multe despre lectură compiler ieșire, a se vedea Q&A, și Matt's CppCon2017 vorbesc: ["Ce Are Compiler Făcut pentru Mine în ultima vreme? Unbolting Compilatorul's Capac"][6],
În FP cod, l's, de obicei, o mare victorie pentru inline funcții mici. Mai ales pe non-Windows, în cazul în care standardul de asteptare a convenției nu a păstrat registre, astfel încât compilatorul poate't păstra orice FP valori în XMM înregistrează peste un "apel". Deci, chiar dacă tu nu't într-adevăr știu așm, încă mai poți vedea cu ușurință dacă-l's doar o coada de apel la funcția de bibliotecă sau dacă inline pentru unul sau doi matematica instrucțiuni. Ceva care inlines la una sau două instrucțiuni este mai bună decât un apel de funcție (în special pentru această sarcină pe x86 sau ARM).
Pe x86, ceva care inlines să SSE4.1 roundsd
poate auto-vectorizare cu SSE4.1 roundpd
(sau AVX vroundpd
). (FP->întreg conversii sunt, de asemenea, disponibil în plin SIMD formă, cu excepția FP->64-bit număr întreg care necesită AVX512.)
std::nearbyint()
: -msse4.1
. -msse4.1 -ffast-matematica
, și numai pe gcc 5.4 și anterior. Mai târziu gcc nu inlines ea (poate s-au't dat seama că unul din imediata biți poate suprima inexacte excepție? Ca's ce zăngănit foloseste, dar mai în vârstă gcc folosește același imediat ca pentru rint
atunci când o face inline-l) std::rint
: -msse4.1
-msse4.1
. (Fără SSE4.1, inlines pentru mai multe instructiuni) -ffast-matematica -msse4.1
. std::rotund
: -ffast-matematica -msse4.1
, care necesită două vector de constante. std::podea
/ std::ceil
/ std::trunc
-msse4.1
-msse4.1
-ffast-matematica -msse4.1
int
/ lung
/ lung
:Aveți două opțiuni aici: folosesc lrint(ca
rintdar se întoarce de mult", sau "lung" pentru " llrint
), sau de a folosi un FP->FP rotunjire funcție și apoi converti la un număr întreg de tip normal (cu trunchiere). Unele compilatoare de a optimiza un mod mai bun decât celălalt.
long l = lrint(x);
int i = (int)rint(x);
Notă: int i = lrint(x)convertește "float" sau "dublu" - > "lung" în primul rând, și apoi trunchiază număr întreg de la
int. Acest lucru face o diferență pentru out-of-range numere întregi: Nedefinit Comportamentul în C++, dar bine definite pentru x86 FP -> int instrucțiuni (care compilatorul va emite excepția cazului în care se vede PARALELE la compilare în timp ce faci constantă de propagare, atunci's-a permis să facă cod care se rupe daca-l's a executat vreodată). Pe x86, un FP->conversie întreg care se revarsă întreg produce
INT_MIN " sau " LLONG_MIN(un pic-model de 0x8000000
sau 64-bit echivalent, cu doar semnul-bit setat). Intel solicită acest "întreg nedeterminată" valoare. (A se vedea a `cvttsd2si manuală intrare, instrucțiuni SSE2 care transformă (cu trunchiere) scalar dublu a semnat întreg. L's disponibil cu 32-bit sau 64-bit integer destinație (în modul pe 64 de biți numai). Nu's, de asemenea, un cvtsd2si(conversia curent cu modul de rotunjire), care este ceea ce am'd ca compilatorul să emită, dar, din păcate ccg și zăngănit câștigat't face asta fără-ffast-matematica
.
De asemenea, feriți-vă că FP de la/la unsigned
int / long este mai puțin eficientă pe x86 (fără AVX512). Conversie de la 32-biți fără semn pe 64-bit masina este destul de ieftin; doar conversia 64-bit semnat și trunchia. Dar altfel's semnificativ mai lent.
:
(int/long)rintinlines să
roundsd/
cvttsd2si. (ratat de optimizare a
cvtsd2si).
lrint` nu't de linie, la toate. -ffast-math
: nici un fel inlines :
(int/long)rintrunde și convertește separat (cu 2 total instrucțiuni de SSE4.1 este activat, în caz contrar, cu o grămadă de cod inline pentru
rint "fără" roundsd).
lrint` nu't inline. -ffast-math
: toate căile inline la cvtsd2si
(optimă), nu este nevoie pentru SSE4.1. :
(int/long)rintinlines la 2 instrucțiuni.
lrint` nu't inline -ffast-math
: (int/long)rint
compilează un apel la lrint
. lrint
nu't inline. Acest lucru poate fi un ratat de optimizare excepția cazului în care cele două instrucțiuni luăm fără-ffast-math` sunt foarte lente. Funcția dublu rotund(dublu) cu utilizarea modf` funcția:
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 compila curat, include "math.h" și "limite" sunt necesare. Funcția funcționează în conformitate cu un text de rotunjire schema:
Nu este nevoie de a pune în aplicare orice, așa că am'm nu sunt sigur de ce atât de multe răspunsuri implica definește, funcții sau metode.
În C99
Avem următoarele și și antet <tgmath.h> de tip generic macro-uri.
#include <math.h>
double round (double x);
float roundf (float x);
long double roundl (long double x);
Dacă nu puteți compila acest lucru, aveți probabil, a plecat din biblioteca matematica. O comandă similară cu aceasta funcționează pe orice compilator C-am (mai multe).
gcc -lm -std=c99 ...
În C++11
Avem următoarele și suprasarcini suplimentare în #includ <cmath> care se bazează pe standardul IEEE dublă precizie în virgulă mobilă.
#include <math.h>
double round (double x);
float round (float x);
long double round (long double x);
double round (T x);
Acolo sunt echivalentele în namespace std prea.
Dacă nu puteți compila acest lucru, puteți utiliza C compilare in loc de C++. Următoarele bază de comandă 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 Comunitate.
g++ -std=c++11 -Wall
Cu Ordinal Divizie
Când împărțirea a două numere ordinale, unde T este short, int, long, sau o altă ordine, rotunjirea exprimare este asta.
T roundedQuotient = (2 * integerNumerator + 1)
/ (2 * integerDenominator);
Precizie
Nu există nici o îndoială că ciudate inexactități apar în operațiunile cu virgulă mobilă, dar aceasta este doar atunci când numerele pare, și are prea puțin de-a face cu rotunjire asupra sumelor.
Sursa nu este doar numărul de cifre semnificative în mantisă de IEEE reprezentarea unui număr în virgulă mobilă, este legată de a noastră zecimal gândire ca oameni.
Zece este produs de cinci și doi, și 5 și 2 sunt relativ prime. Prin urmare, IEEE virgulă mobilă standarde nu pot fi reprezentate perfect ca numere zecimale pentru toate binar de reprezentare digitală.
Aceasta nu este o problemă cu rotunjire algoritmi. Este matematice realitate care ar trebui să fie luate în considerare în timpul de selecție de tipuri și design de calcule, de datele de intrare, și de afișare a numerelor. Dacă o aplicație afișează cifre care arată că aceste zecimal-binar probleme de conversie, atunci cererea este vizual exprimarea precizie care nu există în realitate digital și ar trebui să fie schimbat.
Dacă aveți nevoie pentru a fi capabil de a compila codul în medii care susțin C++11 standard, dar, de asemenea, nevoie pentru a fi capabil de a compila că același cod în medii în care nu't-l sprijine, ai putea folosi o funcție macro pentru a alege între std::rotunde() și o funcție particularizată pentru fiecare sistem. Doar dă-DCPP11 " sau " /DCPP11` la C++11-compatibil compilator (sau de a folosi built-in versiunea macro-uri), și de a face un antet de genul asta:
// 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, a se vedea http://ideone.com/zal709 .
Acest aproximează std::rotunde() în medii în care nu't C++11-conforme, inclusiv conservarea bitul de semn pentru -0.0. Aceasta poate provoca o ușoară hit de performanță, cu toate acestea, și va avea probabil probleme cu rotunjire anumite cunoscut "problema" valori în virgulă mobilă, cum ar fi 0.49999999999999994 sau valori similare.
Alternativ, dacă aveți acces la un C++11-compatibil compiler, ai putea apuca doar std::rotunde() din <cmath>
antet, și să-l utilizați pentru a face propriul antet care definește funcția daca's nu este deja definit. Rețineți că acest lucru nu poate fi o soluție optimă, cu toate acestea, mai ales dacă aveți nevoie pentru a compila pentru mai multe platforme.
Bazat pe Kalaxy's de răspuns, următoarele este un templated soluție runde orice număr în virgulă mobilă la cel mai apropiat număr întreg de tip pe baza naturală de rotunjire. De asemenea, acesta aruncă o eroare în modul de depanare în cazul în care valoarea este în afara intervalului de tip întreg, servind astfel ca aproximativ o viabile funcția de bibliotecă.
// 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);
}
După cum a subliniat în comentariile și alte răspunsuri, ISO C++ standard library nu a adăuga round()
până ISO C++11, atunci când această funcție a fost tras de referință ISO C99 matematica standard de bibliotecă.
Pentru pozitivă operanzi în [½, ub] rotund(x) == floor (x + 0.5) unde *ub* e 2<sup>23</sup> pentru "float" când mapate la IEEE-754 (2008)
binary32, și 2<sup>52</sup> pentru "dubla" atunci când este mapat la IEEE-754 (2008)
binary64. Numerele 23 și 52 corespundă cu numărul de *stocate* mantisa biți în aceste două plutitoare-punct de formate. Pentru pozitivă operanzi în [+0, ½)
round(x) == 0, și pentru pozitivă operanzi în (*ub*, +∞]
rotund(x) == x. Cum funcției este simetric față de axa x, negative argumente " x " pot fi manipulate conform rotund(-x) == -round(x)
.
Acest lucru duce la compact codul de mai jos. Se compilează într-un număr rezonabil de instrucțiuni mașină pe diverse platforme. Am observat cel mai compact codul de pe Gpu-uri, unde my_roundf()` are nevoie de aproximativ o duzină de instrucțiuni. În funcție de arhitectura de procesor și toolchain, acest virgulă mobilă pe bază de abordare ar putea fi mai rapid sau mai lent decât întreg bazat pe punerea în aplicare de newlib face referire într-un alt răspuns.
Am testat my_roundf()
exhaustiv împotriva newlib roundf () punerea în aplicare folosind compilatorul Intel versiunea 13, cu atât
/fp:strict " și " /fp:rapid. Am verificat, de asemenea, că newlib versiune corespunde
roundf () " în " mathimfbiblioteca de la Intel compiler. Testare exhaustivă nu este posibil pentru dublă precizie
round()`, cu toate acestea, codul este structural identică cu single-precizie în aplicare.
#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;
}
Eu folosesc următoarele punerea în aplicare a rundă în asm pentru arhitectura x86 și MS VS specifice C++:
__forceinline int Round(const double v)
{
int r;
__asm
{
FLD v
FISTP r
FWAIT
};
return r;
}
UPD: pentru a reveni 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
Cel mai bun mod de a încheia o valoare variabilă de "n" zecimale, este ca în urma cu O(1) timp:-
Avem de a rotunji valoarea de 3 locuri, adică n=3.Deci,
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");
Ar putea fi ineficient mod murdar de conversie, dar la naiba, funcționează lol. Și-l's bun, deoarece se aplică reale float. Nu afectează doar de ieșire vizual.