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

Author: Post Self, 2009-01-28

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.

 136
Author: Andreas Magnusson,
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.

 92
Author: Daniel Wolf,
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.

 73
Author: Shafik Yaghmour,
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); 
}
 72
Author: kalaxy,
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
 39
Author: schibum,
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.

 28
Author: MSN,
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:

  1. zaokrąglanie konwersji
  2. 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))

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

 13
Author: Sangeet,
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.

 11
Author: Philipp,
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;
}
 6
Author: Carl,
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.

 5
Author: dshin,
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 .

 4
Author: aka.nice,
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 dla makr typu generic.

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

 2
Author: FauChristian,
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ę.]}


Gcc / clang dla x86-64 i AArch64

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

Na x86 wszystko, co jest zgodne z SSE4. 1 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():

    • x86 clang: inlines to a single insn with -msse4.1.
    • W przeciwieństwie do innych języków, w których nie ma żadnego kodu źródłowego, nie ma żadnego kodu źródłowego. Później gcc nigdy go nie wstawia (może nie zdawali sobie sprawy, że jeden z natychmiastowych bitów może stłumić niedokładny wyjątek? To jest to, czego używa clang, ale starsze gcc używa tego samego natychmiastowego jak dla rint, gdy robi inline it)
  • AArch64 gcc6. 3: domyślnie linie do pojedynczego insn.
  • std::rint:

    • x86 clang: inlines to a single insn with -msse4.1
    • x86 gcc7: inlines to a single insn with -msse4.1. (Bez SSE4. 1, inlines to several instructions)
    • x86 gcc6.x i wcześniej: inlines to a single insn with -ffast-math -msse4.1.
    • AArch64 gcc: inlines to a single insn by default
  • std::round:

    • x86 clang: doesn ' t inline
    • x86 gcc: inlines to multiple instructions with -ffast-math -msse4.1, requires two vector Constant.
    • AArch64 gcc: inlines to a single instruction( HW support for this rounding mode as well as IEEE default and most other.)
  • std::floor / std::ceil / std::trunc

    • x86 clang: inlines to a single insn with -msse4.1
    • x86 gcc7.x: inlines to a single insn with -msse4.1
    • x86 gcc6.x i wcześniej: inlines to a single insn with -ffast-math -msse4.1
    • AArch64 gcc: domyślnie inlines to pojedyncza Instrukcja

  • Zaokrąglenie do int / long / long long:

    Masz tu dwie opcje: użyj 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);
    

    Zauważ, że 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).

    Na x86, FP - > integer konwersja, która przepełnia liczbę całkowitą, tworzy 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.

    Uważaj również, że FP to / from 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.

    • X86 clang z / bez -ffast-math -msse4.1: (int/long)rint inlines to roundsd / cvttsd2si. (pominięto optymalizację do cvtsd2si). lrint nie wpisuje się w wszystkie.

    • X86 gcc6.x i wcześniej bez -ffast-math: No way inlines

    • x86 gcc7 bez -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.
    • X86 gcc z -ffast-math: wszystkie sposoby inline do cvtsd2si (optymalny) , nie ma potrzeby stosowania SSE4. 1.

    • AArch64 gcc6. 3 bez -ffast-math: (int/long)rint inlines to 2 instructions. lrint Nie w linii

    • AArch64 gcc6. 3 z -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.

     2
    Author: Peter Cordes,
    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
     2
    Author: konik,
    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.

     1
    Author: Justin Time,
    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);
        }
    
     1
    Author: quant,
    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;
    }
    
     1
    Author: njuffa,
    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
    
     0
    Author: Aleksey F.,
    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.

     -4
    Author: Brad,
    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;
        }
    }
    
     -6
    Author: Peter Mortensen,
    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