round() dla float w C++
Potrzebuję prostej funkcji zaokrąglania zmiennoprzecinkowego, a więc:
double round(double);
round(0.1) = 0
round(-0.1) = 0
round(-0.9) = -1
Mogę znaleźć ceil()
i floor()
w matematyce.h-ale nie round()
.
Czy jest obecny w standardowej bibliotece C++ pod inną nazwą, czy go brakuje??
20 answers
Nie ma funkcji round () w bibliotece standardowej C++98. Możesz sam napisać. Poniżej znajduje się implementacja round-half-up :
double round(double d)
{
return floor(d + 0.5);
}
Prawdopodobnym powodem, dla którego nie ma funkcji okrągłej w bibliotece standardowej C++98 jest to, że może ona być zaimplementowana na różne sposoby. Powyższe jest jednym z powszechnych sposobów, ale są inne, takie jak zaokrąglanie do parzystego , które jest mniej stronnicze i ogólnie lepsze, jeśli masz zamiar zrobić dużo zaokrąglenia; jest to nieco bardziej złożone do zaimplementuj jednak.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-27 09:16:53
Boost oferuje prosty zestaw funkcji zaokrąglania.
#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
Aby uzyskać więcej informacji, zobacz dokumentację Boost.
Edit: od C++11 istnieją std::round
, std::lround
, oraz std::llround
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-25 20:08:15
Standard C++03 opiera się na standardzie C90 dla tego, co standard nazywa standardową biblioteką C, która jest objęta szkicem standardu C++03(najbliższy publicznie dostępny projekt standardu C++03 to N1804) Sekcja 1.2
odniesienia normatywne :
Biblioteka opisana w klauzuli 7 normy ISO / IEC 9899: 1990 oraz w klauzuli 7 ISO / IEC 9899 / Amd.1:1995 jest dalej nazywany standardem C Biblioteka.1)
Jeśli przejdziemy do dokumentacji C Dla round, lround, llround na cppreference widzimy, że roundi powiązane funkcje są częścią C99 i dlatego nie będą dostępne w C++03 lub wcześniej.
W C++11 zmienia się to, ponieważ C++11 opiera się na projekcie standardu C99 dla biblioteki standardowej C i dlatego dostarcza std::round i dla całkowych typów zwrotnych 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 ;
}
Inną opcją również z C99 byłoby std::trunc które:
Oblicza najbliższą liczbę całkowitą nie większą niż 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 ;
}
Jeśli potrzebujesz obsługi aplikacji innych niż C++11, najlepiej byłoby użyć boost round, iround, lround, llround lub boost trunc .
Rolling your own version of round is hard
Kręcenie własnym nie jest prawdopodobnie warte wysiłku, ponieważ trudniejsze niż wygląda: zaokrąglenie float do najbliższej liczby całkowitej, Część 1, Zaokrąglanie float do najbliższej liczby całkowitej, część 2 i Zaokrąglanie float do najbliższej liczby całkowitej, część 3 wyjaśnij:
Na przykład wspólna rolka implementacji za pomocą std::floor
i dodanie 0.5
nie działa dla wszystkich wejść:
double myround(double d)
{
return std::floor(d + 0.5);
}
One input this will fail for is 0.49999999999999994
, (zobacz to na żywo).
Inna wspólna implementacja polega na przerzuceniu typu zmiennoprzecinkowego na całkę typ, który może wywoływać nieokreślone zachowanie w przypadku, gdy integralna część nie może być reprezentowana w typie docelowym. Możemy to zobaczyć z sekcji draft C++ standard 4.9
Floating-integral conversions czyli (podkreślenie):
Wartość prvalue typu zmiennoprzecinkowego może być przekonwertowana na wartość prvalue Typ integer. Przemiana obcina, czyli część ułamkowa jest odrzucany. zachowanie jest niezdefiniowane, jeśli okrojone wartość nie może być reprezentowane w typie docelowym.[...]
Na przykład:
float myround(float f)
{
return static_cast<float>( static_cast<unsigned int>( f ) ) ;
}
Podane std::numeric_limits<unsigned int>::max()
jest 4294967295
wtedy wywołanie:
myround( 4294967296.5f )
Spowoduje przepełnienie, (zobacz to na żywo).
Możemy zobaczyć, jak trudne jest to naprawdę patrząc na tę odpowiedź zwięzły sposób implementacji round () w C? które odwołują się do newlibs wersja single precision float round. Jest to bardzo długa funkcja dla coś, co wydaje się proste. Wydaje się mało prawdopodobne, aby ktokolwiek bez intymnej wiedzy o implementacjach zmiennoprzecinkowych mógł poprawnie zaimplementować tę funkcję: {]}
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;
}
Z drugiej strony, jeśli żadne z innych rozwiązań nie jest użyteczne newlib może potencjalnie być opcją, ponieważ jest to dobrze przetestowana implementacja.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:11
Warto zauważyć, że jeśli chcesz uzyskać wynik całkowity z zaokrąglenia, nie musisz go przepuszczać ani przez sufit, ani przez podłogę. I. e.,
int round_int( double r ) {
return (r > 0.0) ? (r + 0.5) : (r - 0.5);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-01-11 16:45:51
Jest dostępny od C++11 w cmath (zgodnie z 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;
}
Wyjście:
round(0.5): 1
round(-0.5): -1
round(1.4): 1
round(-1.4): -1
round(1.6): 2
round(-1.6): -2
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-06-19 13:23:09
Jest zwykle zaimplementowany jako floor(value + 0.5)
.
Edit: i prawdopodobnie nie nazywa się to zaokrągleniem, ponieważ istnieją co najmniej trzy algorytmy zaokrąglania, które znam: zaokrąglenie do zera, zaokrąglenie do najbliższej liczby całkowitej i zaokrąglenie bankiera. Prosisz o zaokrąglenie do najbliższej liczby całkowitej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-07-03 14:57:53
Są 2 problemy, na które patrzymy:
- zaokrąglanie konwersji
- konwersja typu.
Konwersje zaokrąglenia oznaczają zaokrąglenie ± float / double do najbliższej podłogi/float / double. Może twój problem się tutaj skończy. Ale jeśli oczekujesz powrotu Int / Long, musisz wykonać konwersję typu, a tym samym problem "przepełnienia" może trafić w Twoje rozwiązanie. Tak więc, sprawdź błąd w funkcji
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))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-29 12:37:29
Pewien rodzaj zaokrąglenia jest również zaimplementowany w 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;
}
Zauważ, że działa to tylko wtedy, gdy wykonasz konwersję do-integer.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-29 13:02:15
Można zaokrąglić do n cyfr z dokładnością:
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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-12 03:02:23
Jeśli ostatecznie chcesz przekonwertować double
wyjście swojej funkcji round()
na int
, wtedy przyjęte rozwiązania tego pytania będą wyglądać mniej więcej tak:
int roundint(double r) {
return (int)((r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5));
}
To zegary w około 8.88 ns na mojej maszynie, gdy przekazywane w równomiernie losowych wartościach.
Poniżej znajduje się funkcjonalny odpowiednik, o ile mogę powiedzieć, ale zegary w 2.48 NS na mojej maszynie, dla znaczącej przewagi wydajności:int roundint (double r) {
int tmp = static_cast<int> (r);
tmp += (r-tmp>=.5) - (r-tmp<=-.5);
return tmp;
}
Wśród powodów lepszego wydajność to pominięcie rozgałęzienia.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-03 03:02:31
Strzeż się floor(x+0.5)
. Oto co może się zdarzyć dla liczb nieparzystych w zakresie [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
To jest http://bugs.squeak.org/view.php?id=7134 . Użyj rozwiązania takiego jak @konik.
Moja własna solidna wersja byłaby czymś w stylu:
double round(double x)
{
double truncated,roundedFraction;
double fraction = modf(x, &truncated);
modf(2.0*fraction, &roundedFraction);
return truncated + roundedFraction;
}
Kolejny powód, aby uniknąć podłogi (x+0.5) podano tutaj .
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-03 03:00:31
Nie ma potrzeby implementowania czegokolwiek, więc nie jestem pewien, dlaczego tak wiele odpowiedzi zawiera definicje, funkcje lub metody.
W C99
Mamy następujący nagłówek and and
#include <math.h>
double round (double x);
float roundf (float x);
long double roundl (long double x);
Jeśli nie możesz tego skompilować, prawdopodobnie pominąłeś bibliotekę matematyczną. Polecenie podobne do tego działa na każdym kompilatorze C jaki posiadam (kilka).
gcc -lm -std=c99 ...
W C++11
Mamy następujące i dodatkowe przeciążenia w # include które opierają się na zmiennoprzecinkowej podwójnej precyzji IEEE.
#include <math.h>
double round (double x);
float round (float x);
long double round (long double x);
double round (T x);
Istnieją również odpowiedniki w przestrzeni nazw std.
Jeśli nie możesz tego skompilować, możesz używać kompilacji C zamiast c++. Poniższe polecenie podstawowe nie powoduje błędów ani ostrzeżeń w 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
Z Podziałem Porządkowym
Dzieląc dwie liczby porządkowe, gdzie T jest krótkie, int, długie lub inne porządkowe, wyrażenie zaokrąglające jest takie.
T roundedQuotient = (2 * integerNumerator + 1)
/ (2 * integerDenominator);
Dokładność
Nie ma wątpliwości, że dziwnie wyglądające nieścisłości pojawiają się w operacjach zmiennoprzecinkowych, ale dzieje się tak tylko wtedy, gdy pojawiają się Liczby i ma niewiele wspólnego z zaokrągleniem.
Źródłem nie jest tylko liczba znaczących cyfr w mantysie reprezentacji IEEE liczby zmiennoprzecinkowej, to jest związane z naszym dziesiętnym myśleniem jako ludzi.
Ten jest iloczyn 5 i 2 oraz 5 i 2 są względnie pierwsze. Dlatego też standardy zmiennoprzecinkowe IEEE nie mogą być doskonale reprezentowane jako liczby dziesiętne dla wszystkich binarnych reprezentacji cyfrowych.
To nie jest problem z algorytmami zaokrąglania. Jest to rzeczywistość matematyczna, która powinna być brana pod uwagę podczas wyboru typów i projektowania obliczeń, wprowadzania danych i wyświetlania liczb. Jeśli aplikacja wyświetla cyfry, które pokazują te dziesiętne-binarne konwersji problemy, to aplikacja wizualnie wyraża dokładność, która nie istnieje w cyfrowej rzeczywistości i powinna zostać zmieniona.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-04 09:13:16
W dzisiejszych czasach nie powinno być problemu użycie kompilatora C++11, który zawiera bibliotekę matematyczną C99/C++11. Ale wtedy pojawia się pytanie: którą funkcję zaokrąglania wybierasz?
C99 / C++11 round()
często nie jest funkcją zaokrąglania, którą chcesz . Używa funkowego trybu zaokrąglania, który zaokrągla się od 0 jako tie-break w przypadku pół drogi (+-xxx.5000
). Jeśli zależy ci na trybie zaokrąglania lub kierujesz implementację C++, w której round()
jest szybsza niż rint()
, następnie użyj go (lub naśladuj jego zachowanie z jedną z innych odpowiedzi na to pytanie, które przyjęło go jako wartość nominalną i starannie odtworzyło to konkretne zaokrąglenie.)
round()
'zaokrąglenie s różni się od domyślnej rundy IEEE754 do najbliższego trybu, nawet jako tie-break . Nearest-even unika błędu statystycznego w średniej wielkości liczb, ale ma błąd w stosunku do liczb parzystych.
Istnieją dwie biblioteki matematyczne funkcji zaokrąglania, które używają bieżącego domyślny tryb zaokrąglania: std::nearbyint()
oraz std::rint()
, oba dodane w C99/C++11, więc są dostępne w każdej chwili std::round()
jest. Jedyną różnicą jest to, że nearbyint
nigdy nie podnosi FE_INEXACT.
W 1999 roku, w wyniku połączenia z gcc, GCC i clang, GCC i clang zostały połączone w jedną linię, a gcc nigdy nie połączyły się w jedną linię.]}
Dodałem kilka funkcji testowych na Eksploratorze kompilatora Matta Godbolta , gdzie można zobaczyć źródło + wyjście asm (dla wielu kompilatorów). Aby dowiedzieć się więcej o odczytywaniu danych wyjściowych kompilatora, zobacz to Q&A oraz rozmowę Matta CppCon2017: "co mój kompilator zrobił dla mnie ostatnio? Rozpięcie pokrywy kompilatora", W kodzie FP, zwykle duża wygrana dla małych funkcji wbudowanych. W szczególności w systemach innych niż Windows, gdzie standardowa konwencja wywołania nie ma rejestrów zachowanych przez wywołania, więc kompilator nie może przechowywać żadnych wartości FP w rejestrach XMM w obrębie Na x86 wszystko, co jest zgodne z SSE4. 1 Masz tu dwie opcje: użyj Zauważ, że Na x86, FP - > integer konwersja, która przepełnia liczbę całkowitą, tworzy Uważaj również, że FP to / from X86 clang z / bez X86 gcc6.x i wcześniej bez X86 gcc z AArch64 gcc6. 3 bez
Gcc / clang dla x86-64 i AArch64
call
. Więc nawet jeśli tak naprawdę nie znasz asm, nadal możesz łatwo zobaczyć, czy jest to tylko ogonowe wywołanie funkcji bibliotecznej, czy też jest wbudowane w jedną lub dwie instrukcje matematyczne. Wszystko, co łączy się z jedną lub dwiema instrukcjami, jest lepsze niż wywołanie funkcji (dla tego konkretnego zadania na x86 lub ARM).roundsd
może automatycznie wektoryzować za pomocą SSE4.1 roundpd
(lub AVX vroundpd
). (Konwersje FP - >integer są również dostępne w postaci packed SIMD, z wyjątkiem FP- > 64-bitowa liczba całkowita, która wymaga AVX512.)
std::nearbyint()
:
-msse4.1
.rint
, gdy robi inline it) std::rint
:
-msse4.1
-msse4.1
. (Bez SSE4. 1, inlines to several instructions)-ffast-math -msse4.1
.std::round
:
-ffast-math -msse4.1
, requires two vector Constant.std::floor
/ std::ceil
/ std::trunc
-msse4.1
-msse4.1
-ffast-math -msse4.1
Zaokrąglenie do
int
/ long
/ long long
:lrint
(jak rint
, ale zwraca long
, lub long long
dla llrint
), lub użyj funkcji zaokrąglania FP - >FP, a następnie przekonwertuj na typ integer w normalny sposób (z okrojeniem). Niektóre Kompilatory zoptymalizuj jeden sposób lepiej niż drugi.long l = lrint(x);
int i = (int)rint(x);
int i = lrint(x)
konwertuje float
lub double
-> long
najpierw, a następnie obraca liczbę całkowitą do int
. To robi różnicę dla liczb całkowitych poza zakresem: nieokreślone zachowanie w C++, ale dobrze zdefiniowane dla instrukcji x86 FP - > int (które kompilator będzie emitował, chyba że zobaczy UB w czasie kompilacji, wykonując stałą propagację, wtedy może tworzyć kod, który się łamie, jeśli kiedykolwiek zostanie wykonany).INT_MIN
lub LLONG_MIN
(bitowy wzór 0x8000000
lub odpowiednik 64-bitowy, z ustawieniem bitów znakowych). Intel nazywa tę wartość "liczbą całkowitą nieokreśloną". (Zobacz Instrukcja cvttsd2si
, Instrukcja SSE2, która konwertuje (z okrojeniem) Skalar double do signed integer. Jest dostępny z 32-bitowym lub 64-bitowym miejscem docelowym (tylko w trybie 64-bitowym). Istnieje również cvtsd2si
(Konwertuj z bieżącym trybem zaokrąglania), czyli to, co chcielibyśmy kompilator do emisji, ale niestety gcc i clang nie zrobią tego bez -ffast-math
.unsigned
int / long jest mniej wydajny na x86 (bez AVX512). Konwersja na 32-bitowy niepodpisany na 64-bitowym komputerze jest dość tania; wystarczy przekonwertować na 64-bitowy podpisany i obciąć. Ale poza tym jest znacznie wolniejszy.
-ffast-math -msse4.1
: (int/long)rint
inlines to roundsd
/ cvttsd2si
. (pominięto optymalizację do cvtsd2si
). lrint
nie wpisuje się w wszystkie.-ffast-math
: No way inlines -ffast-math
: (int/long)rint
rund i konwertuje oddzielnie (z 2 instrukcjami SSE4.1 jest włączona, w przeciwnym razie z pakietem kodu inlined dla rint
bez roundsd
). lrint
nie jest w linii.-ffast-math
: wszystkie sposoby inline do cvtsd2si
(optymalny) , nie ma potrzeby stosowania SSE4. 1.-ffast-math
: (int/long)rint
inlines to 2 instructions. lrint
Nie w linii -ffast-math
: (int/long)rint
kompiluje do wywołania lrint
. lrint
nie jest w linii. Może to być pominięta optymalizacja, chyba że dwie instrukcje, które otrzymujemy bez -ffast-math
, są bardzo powolne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-17 10:08:00
Funkcja double round(double)
z wykorzystaniem funkcji modf
:
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);
}
Aby być czystym kompilatorem, zawiera "math.h "i" limity " są konieczne. Funkcja działa według następującego schematu zaokrąglania:
- Runda 5.0 to 5.0
- Runda 3.8 to 4.0
- Runda 2.3 to 2.0
- Runda 1.5 to 2.0
- Runda 0.501 to 1.0
- Runda 0.5 to 1.0
- Runda 0.499 to 0.0
- Runda 0.01 to 0.0
- Runda 0.0 jest 0.0
- Runda -0.01 to -0.0
- Runda -0.499 to -0.0
- Runda -0,5 to -0,0
- Runda -0.501 to -1.0
- Runda -1,5 to -1,0
- Runda -2,3 to -2,0
- Runda -3.8 to -4.0
- Runda -5,0 to -5,0
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-03 02:58:52
Jeśli chcesz mieć możliwość kompilacji kodu w środowiskach, które obsługują standard C++11, ale także musisz mieć możliwość kompilacji tego samego kodu w środowiskach, które go nie obsługują, możesz użyć makra funkcji do wyboru pomiędzy STD:: round () a funkcją niestandardową dla każdego systemu. Wystarczy przekazać -DCPP11
lub /DCPP11
do kompilatora zgodnego z C++11 (lub użyć jego wbudowanych makr wersji) i utworzyć nagłówek w następujący sposób:
// 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 */
Aby uzyskać szybki przykład, zobacz http://ideone.com/zal709 .
To przybliża std:: round () w środowiskach, które nie są zgodne z C++11, włączając zachowanie bitu znaku dla -0.0. Może to jednak spowodować niewielki spadek wydajności i prawdopodobnie będzie mieć problemy z zaokrągleniem pewnych znanych" problematycznych " wartości zmiennoprzecinkowych, takich jak 0.49999999999999999999999999999994 lub podobnych wartości.
Alternatywnie, jeśli masz dostęp do kompilatora zgodnego z C++11, możesz po prostu pobrać std::round () z jego nagłówka <cmath>
i użyć go do stworzenia własnego nagłówka, który definiuje funkcję, jeśli nie jest jeszcze zdefiniowana. Zauważ, że może to nie być optymalne rozwiązanie, szczególnie jeśli musisz kompilować dla wielu platform.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-16 00:59:22
Na podstawie odpowiedzi Kalaxy ' ego, poniżej przedstawiono rozwiązanie wzorcowe, które zaokrągla dowolną liczbę zmiennoprzecinkową do najbliższej liczby całkowitej opartej na naturalnym zaokrągleniu. Wyświetla również błąd w trybie debugowania, jeśli wartość jest poza zakresem typu integer, w ten sposób służąc mniej więcej jako sprawna funkcja biblioteczna.
// 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);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-31 00:08:04
Jak zaznaczono w komentarzach i innych odpowiedziach, biblioteka standardowa ISO C++ nie dodała round()
aż do ISO C++11, Kiedy ta funkcja została wciągnięta przez odniesienie do standardowej biblioteki matematycznej ISO C99.
Dla operandów dodatnich w [ ½ , ub] round(x) == floor (x + 0.5)
, gdzie ub jest 223 dla float
po zmapowaniu do IEEE-754 (2008) binary32
, oraz 252 dla double
gdy jest mapowana do IEEE-754 (2008) binary64
. Liczby 23 i 52 odpowiadają liczbie zapisanej bity mantissa w tych dwóch formatach zmiennoprzecinkowych. Dla dodatnich operandów w [+0,½) round(x) == 0
, oraz dla dodatnich operandów w (ub, +∞] round(x) == x
. Ponieważ funkcja jest symetryczna względem osi x, argumenty ujemne x
mogą być obsługiwane zgodnie z round(-x) == -round(x)
.
Prowadzi to do zwartego kodu poniżej. Kompiluje się w rozsądną liczbę instrukcji maszynowych na różnych platformach. Zaobserwowałem najbardziej zwarty kod na GPU, gdzie my_roundf()
wymaga około tuzina instrukcji. W zależności od architektura procesora i łańcuch narzędzi, to podejście zmiennoprzecinkowe może być szybsze lub wolniejsze niż implementacja oparta na liczbach całkowitych z newlib, o której mowa w innej odpowiedzi .
Przetestowałem my_roundf()
wyczerpująco na nową implementację roundf()
przy użyciu kompilatora Intela w wersji 13, zarówno /fp:strict
jak i /fp:fast
. Sprawdziłem też, że nowa wersja jest zgodna z roundf()
w bibliotece mathimf
kompilatora Intela. Wyczerpujące testy nie są możliwe dla double-precision round()
, jednak kod jest strukturalnie identyczny z implementacją single-precision.
#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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-17 04:23:04
Używam następującej implementacji round W ASM dla architektury x86 I MS VS C++:
__forceinline int Round(const double v)
{
int r;
__asm
{
FLD v
FISTP r
FWAIT
};
return r;
}
UPD: aby zwrócić podwójną wartość
__forceinline double dround(const double v)
{
double r;
__asm
{
FLD v
FRNDINT
FSTP r
FWAIT
};
return r;
}
Wyjście:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-04 15:19:01
// 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");
To może być nieefektywny brudny sposób konwersji, ale heck, to działa lol. I to jest dobre, ponieważ odnosi się do rzeczywistego pływaka. Nie tylko wizualnie.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-03 02:58:01
Zrobiłem to:
#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;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-03 02:56:36