Jak używać stałej PI w C ++

476

Chcę użyć funkcji PI i funkcji trygonometrycznych w niektórych programach C ++. Dostaję funkcje trygonometryczne z include <math.h>. Wydaje się jednak, że w tym pliku nagłówkowym nie ma definicji PI.

Jak mogę uzyskać PI bez ręcznego definiowania?

Etan
źródło
3
@tiwo, pytasz, jaka jest różnica między 3.14, 3.141592a atan(1) * 4?
Nikola Malešević,
21
Na marginesie, cmath powinien być używany w C ++ zamiast matematyki.h, co dotyczy C.
juzzlin
4
Luźno powiązane: patrz cise.ufl.edu/~manuel/obfuscate/pi.c, w jaki sposób obliczyć wartość PI bezpośrednio z definicji.
lorro
2
Przybył w C ++ 20! stackoverflow.com/a/57285400/895245
Ciro Santilli 31 冠状 病 六四 事件 法轮功

Odpowiedzi:

537

Na niektórych (szczególnie starszych) platformach (patrz komentarze poniżej) może być konieczne

#define _USE_MATH_DEFINES

a następnie dołącz niezbędny plik nagłówka:

#include <math.h>

a wartość pi można uzyskać poprzez:

M_PI

W moim math.h(2014) jest to zdefiniowane jako:

# define M_PI           3.14159265358979323846  /* pi */

ale sprawdź math.hwięcej. Fragment „starego” math.h(w 2009 r.):

/* Define _USE_MATH_DEFINES before including math.h to expose these macro
 * definitions for common math constants.  These are placed under an #ifdef
 * since these commonly-defined names are not part of the C/C++ standards.
 */

Jednak:

  1. na nowszych platformach (przynajmniej na moim 64-bitowym Ubuntu 14.04) nie muszę definiować _USE_MATH_DEFINES

  2. Na (najnowszych) platformach Linux są long doubleteż wartości dostarczane jako rozszerzenie GNU:

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */
Ferenc Deak
źródło
51
#define _USE_MATH_DEFINESa następnie #include <math.h>definicje M_PIw Visual C ++. Dzięki.
Etan,
3
Działa również z nagłówkami cygwin.
Rob
24
Zawsze możesz dołączyć cmathzamiast math.h.
Richard J. Ross III
10
Nawet po określeniu, _USE_MATH_DEFINESczy GCC narzeka, że ​​to dlatego, że __STRICT_ANSI__jest zdefiniowane (być może zdałeś -pedanticlub -std=c++11zdałeś), co nie pozwala M_PIna zdefiniowanie, dlatego nie zdefiniuj go -D__STRICT_ANSI__. Definiując go sam, ponieważ jest to C ++, zamiast makra powinieneś constexpr auto M_PI = 3.14159265358979323846;.
legends2k
1
Począwszy od 2018 r., Odpowiedź powinna zostać definitywnie zaktualizowana, tak aby używała <cmath> zamiast <math.h>
jaskmar
170

Pi można obliczyć jako atan(1)*4. Możesz w ten sposób obliczyć wartość i zapisać ją w pamięci podręcznej.

Konamiman
źródło
78
Dla użytkowników c ++ 11:constexpr double pi() { return std::atan(1)*4; }
Matiu
41
-1: Działa tylko wtedy, gdy atan(1)*4 == 3.141592653589793238462643383279502884(z grubsza). Nie postawiłbym na to. Bądź normalny i użyj surowego literału, aby zdefiniować stałą. Po co tracić precyzję, kiedy nie jest to konieczne?
Thomas Eding
29
Można uniknąć operacji mnożenia za pomocą atan2(0, -1);.
legends2k
44
@matiu atannie jest constexpr.
R. Martinho Fernandes,
45
acos(-1)Zamiast tego spróbuj atan2.
user541686,
113

Możesz także użyć wzmocnienia, które definiuje ważne stałe matematyczne z maksymalną dokładnością dla żądanego typu (tj. Zmiennoprzecinkowe vs podwójne).

const double pi = boost::math::constants::pi<double>();

Sprawdź dokumentację doładowania, aby uzyskać więcej przykładów.

BuschnicK
źródło
184
Boost: Zwiększenie i tak już niepotrzebnej złożoności C ++ od 1999 roku!
Dan Molding
47
Chwytliwy i częściowo prawdziwy. Z drugiej strony doładowanie może być fenomenalnie przydatne czasami ...
BuschnicK
59
@DanMoulding: Uhm. Czy C to jedyny znany język? Ponieważ wszystkie inne języki, które znam, oprócz C, mają standardową bibliotekę o wielkości większej niż C ++ '(np. Python, Haskell, C #, PHP, Delphi, Erlang, Java, ......). Z własnego doświadczenia wynika, że ​​ta elitarna not gonna use libsopinia jest szkodnikiem i prawdopodobnie głównym powodem złego oprogramowania napisanego w C ++.
Sebastian Mach
11
@Gracchus: Tak. C ++ bez bibliotek (lub bez nowych bibliotek C ++ 11) jest tak samo, jak lubię ten język i tak bardzo, jak sam chciałbym wszystko kodować, nie jest zbyt produktywny.
Sebastian Mach
14
Myślę, że powiedział, że złożoność, a nie rozmiar . Przypuszczalnie odnosząc się do a) 3 zagnieżdżonych przestrzeni nazw, oraz b) definiowania pi jako funkcji szablonowej zamiast zwykłej stałej.
Timmmm
83

Zamiast tego pobierz go z jednostki FPU na chipie:

double get_PI()
{
    double pi;
    __asm
    {
        fldpi
        fstp pi
    }
    return pi;
}

double PI = get_PI();
Henrik
źródło
40
:-) prawdopodobnie nie ta niezależna od platformy, ale fajne dodatkowe egzotyczne rozwiązanie!
Etan
3
uwielbiam to, co
wyszliśmy
1
Uwielbiam tę odpowiedź. Jest to szczególnie przydatne, gdy atakuje się starsze platformy x86, co jest ostatnio małą modą, gdy optymalizujące kompilatory nie są tak strasznie zaangażowane jak nowoczesne. Dzięki za to Henrik!
Matt
49

Polecam po prostu wpisać pi z dokładnością, jakiej potrzebujesz. To nie zwiększy czasu wykonywania obliczeń i będzie przenośne bez użycia nagłówków lub # definicji. Obliczanie acos lub atan jest zawsze droższe niż stosowanie wstępnie obliczonej wartości.

const double PI  =3.141592653589793238463;
const float  PI_F=3.14159265358979f;
Alex
źródło
28
To świetny przykład, dlaczego nie powinniśmy przyjmować tego podejścia, my popełniamy błędy, zaokrąglamy, kopiujemy i wklejamy itp. Myślę, że używanie M_PI jest właściwym podejściem.
nacho4d
10
Jeśli robi się to w C ++ 11, wykonaj consta constexpr.
legends2k
3
@ nacho4d Ja też wolę M_PI, jeśli jest dostępny, ale nie wszystkie systemy są zgodne z POSIX. Myślę, że to podejście jest lepsze niż metoda 4 * atan (1) w przypadkach, w których M_PI nie jest dostępne.
m24p
2
„Obliczanie acos lub atan jest zawsze droższe” nie jest prawdą. Każdy nowoczesny kompilator optymalizacyjny wie wszystko o standardowych funkcjach matematycznych i może się przez nie rozprzestrzeniać. Patrz np. Goo.gl/BvdJyr
Nemo
2
@Nemo, Counter example: godbolt.org/g/DsAern Jak powiedziano gdzie indziej, wydaje się, że robi to tylko GCC i prawdopodobnie dlatego, że zadeklarował podstawowe funkcje matematyczne jako constexpr.
Parker Coates
47

Zamiast pisać

#define _USE_MATH_DEFINES

Polecam używanie -D_USE_MATH_DEFINESlub /D_USE_MATH_DEFINESzależnie od twojego kompilatora.

W ten sposób masz pewność, że nawet w przypadku, gdy ktoś włączy nagłówek, zanim to zrobisz (i bez #define) nadal będziesz mieć stałe zamiast niejasnego błędu kompilatora, którego wyśledzenie zajmie Ci całe wieki.

Matthieu M.
źródło
Dobra wskazówka. Jeśli „jesteś” to jednostka kompilująca, możesz oczywiście upewnić się, że makro jest zdefiniowane, zanim cokolwiek zostanie dołączone. Ale jeśli „ty” jest plikiem nagłówkowym, jest poza twoją kontrolą.
Steve Jessop
3
W rzeczywistości, nawet jeśli „ty” jest jednostką kompilacyjną ... w zależności od kolejności nagłówków jest to najkrótsza droga do koszmaru konserwacji ...
Matthieu M.,
1
Nie musisz jednak polegać na kolejności nagłówków. Nie ma znaczenia, czy nagłówki zawierają się nawzajem, pod warunkiem, że wykonasz #define zanim # cokolwiek w ogóle załączysz (przynajmniej przy założeniu, że nic # nie definiuje tego). To samo dotyczy NDEBUG.
Steve Jessop
1
Bardzo częstym problemem w projekcie jest to, że jeśli na przykład kompilujesz za pomocą Visual Studio, nie wiesz, w jakiej kolejności kompilator będzie przechodził przez twoje pliki, więc jeśli używasz go <cmath>w różnych miejscach, staje się to dużym problemem (szczególnie jeśli jest dołączony przez inną bibliotekę, którą dołączasz). Byłoby znacznie lepiej, gdyby umieścili tę część poza osłonami hedera, ale cóż, teraz niewiele można na to poradzić. Dyrektywa kompilatora działa naprawdę całkiem dobrze.
meneldal
40

Ponieważ oficjalna biblioteka standardowa nie definiuje stałego PI, musiałbyś go zdefiniować sam. Więc odpowiedź na twoje pytanie „Jak mogę uzyskać PI bez definiowania go ręcznie?” to „Nie - lub polegasz na niektórych rozszerzeniach specyficznych dla kompilatora”. Jeśli nie martwisz się o przenośność, możesz to sprawdzić w instrukcji kompilatora.

C ++ pozwala pisać

const double PI = std::atan(1.0)*4;

ale nie można zagwarantować, że inicjalizacja tej stałej będzie statyczna. Kompilator G ++ obsługuje jednak te funkcje matematyczne jako elementy wewnętrzne i jest w stanie obliczyć to stałe wyrażenie w czasie kompilacji.

sellibitze
źródło
6
Zwykle używam acos (-1), jak mówisz, są one oceniane w czasie kompilacji. Kiedy testowałem M_PI, acos (-1) i atan (1) * 4, otrzymałem identyczne wartości.
Micheasza
2
Tradycyjnym sposobem jest użycie 4*atan(1.): atanjest łatwy do wdrożenia, a pomnożenie przez 4 jest dokładną operacją. Oczywiście współczesne kompilatory składają (dążą do złożenia) wszystkie stałe z wymaganą precyzją i jest całkowicie rozsądne w użyciu, acos(-1)a nawet std::abs(std::arg(std::complex<double>(-1.,0.)))odwrotne do formuły Eulera, a zatem bardziej estetyczne niż się wydaje (dodałem, absponieważ nie „ pamiętaj, w jaki sposób wycina się płaszczyznę złożoną lub jeśli jest to w ogóle zdefiniowane).
tobi_s
Właśnie dlatego nikt przypadkowo nie myśli, że mówisz poważnie (ponownie -_- '). To okropne rozwiązanie. Implementacja atan nie jest zdefiniowana przez standard, co oznacza jego implementację i być może zależna od hw. Oznacza to, że wartości liczbowe mogą być okropne, co oznacza, że ​​lepiej jest używać ogólnie 3.14. Co więcej, jest to prawdopodobnie powolne, nawet w szczególnych przypadkach.
midjji
32

Ze strony podręcznika Posix math.h :

   The  <math.h>  header  shall  provide for the following constants.  The
   values are of type double and are accurate within the precision of  the
   double type.

   M_PI   Value of pi

   M_PI_2 Value of pi/2

   M_PI_4 Value of pi/4

   M_1_PI Value of 1/pi

   M_2_PI Value of 2/pi

   M_2_SQRTPI
          Value of 2/ sqrt pi
Joakim
źródło
3
Dobra odpowiedź, ale link nie działa. Zamiast tego sugeruję ten .
Abderrahim Kitouni
30

C ++ 20 std::numbers::pi

W końcu dotarł: http://eel.is/c++draft/numbers

Oczekuję, że użycie będzie takie jak:

#include <numbers>
#include <iostream>

int main() {
    std::cout << std::numbers::pi << std::endl;
}

Spróbuję, kiedy wsparcie dotrze do GCC, GCC 9.1.0 z g++-9 -std=c++2a nadal go nie obsługuje.

Przyjęta propozycja opisuje:

5.0 „Nagłówki” [nagłówki] W tabeli [tab: cpp.library.headers] nowy<math> nagłówek.

[...]

namespace std {
namespace math { 
  template<typename T > inline constexpr T pi_v = unspecified;
    inline constexpr double pi = pi_v<double>;

Jest też std::numbers::eoczywiście :-) Jak obliczyć stałą Eulera lub Eulera zasilanego w C ++?

Te stałe używają funkcji szablonu zmiennej C ++ 14: Szablony zmiennych C ++ 14: jaki jest ich cel? Dowolny przykład użycia?

We wcześniejszych wersjach projektu stała była pod std::math::pi: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf

Ciro Santilli
źródło
27

Standardowy C ++ nie ma stałej dla PI.

Wiele kompilatorów C ++ definiuje M_PIw cmath(lub math.hdla C) jako niestandardowe rozszerzenie. Być może będziesz musiał #define _USE_MATH_DEFINESto zobaczyć.

RichieHindle
źródło
18

chciałbym zrobić

template<typename T>
T const pi = std::acos(-T(1));

lub

template<typename T>
T const pi = std::arg(-std::log(T(2)));

Chciałbym nie wpisując Õ do precyzji trzeba . Co to w ogóle ma znaczyć? Precyzja potrzebne jest precyzja T, ale nic o tym wiedzieć T.

Możesz powiedzieć: o czym ty mówisz? Tbędzie float, doublealbo long double. Więc po prostu wpisz dokładność long double, tj

template<typename T>
T const pi = static_cast<T>(/* long double precision π */);

Ale czy naprawdę wiesz, że w przyszłości nie będzie nowego typu zmiennoprzecinkowego z jeszcze większą precyzją niż long double? Ty nie.

I dlatego pierwsze rozwiązanie jest piękne. Możesz być całkiem pewien, że standard przeciąża funkcje trygonometryczne dla nowego typu.

I proszę, nie mów, że ocena funkcji trygonometrycznych przy inicjalizacji jest ograniczeniem wydajności.

0xbadf00d
źródło
1
Pamiętaj, że arg(log(x)) == πdla wszystkich 0 < x < 1.
0xbadf00d
To okropny pomysł. użyj przeciążonego szablonu typu constexpr, w ten sposób otrzymasz błąd kompilacji, aby zmusić cię do zdefiniowania go, jeśli pojawi się nowy typ. Jest to również ogólnie okropne, ponieważ typy wyzwalaczy nie są ograniczone do typów zmiennoprzecinkowych. Ciesz się więc błędem atan (1) ... Norma nie gwarantuje, że funkcje trygonometryczne obliczą swoje rzeczywiste wartości trygonometryczne z dokładnością do typu. Na ogół nie, i pogarsza się np. Z szybką matematyką i zawsze jest szczególnie niekorzystny dla specjalnych wartości.
midjji
10

Używam następujących w jednym z moich wspólnych nagłówków w projekcie, który obejmuje wszystkie podstawy:

#define _USE_MATH_DEFINES
#include <cmath>

#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

#ifndef M_PIl
#define M_PIl (3.14159265358979323846264338327950288)
#endif

Na marginesie, wszystkie poniższe kompilatory definiują stałe M_PI i M_PIl, jeśli je uwzględnisz <cmath>. Nie ma potrzeby dodawania `#define _USE_MATH_DEFINES, który jest wymagany tylko w VC ++.

x86 GCC 4.4+
ARM GCC 4.5+
x86 Clang 3.0+
Shital Shah
źródło
Czy downvoter może skomentować, co jest nie tak z tą odpowiedzią. Jest to dobrze zbadane i przetestowane i używane w prawdziwym systemie. Zdecydowanie chciałem to poprawić, jeśli coś jest nie tak.
Shital Shah,
1
Do Twojej wiadomości, kompilatory Borland C ++ również definiują M_PIbez potrzeby_USE_MATH_DEFINES
Remy Lebeau
8

Generalnie wolę zdefiniować własne: const double PI = 2*acos(0.0); ponieważ nie wszystkie implementacje to zapewniają.

Pytanie, czy funkcja ta jest wywoływana w czasie wykonywania, czy jest statycznie wyłączana w czasie kompilacji, zwykle nie stanowi problemu, ponieważ dzieje się tak tylko raz.

Sumudu Fernando
źródło
8
acos (-1) to także pi.
Roderick Taylor,
3
Często jest mniej instrukcji procesora i / lub mniej opóźnień, aby załadować operand natychmiastowy niż odczytać operand z pamięci. Ponadto, tylko wyrażenia znane w czasie kompilacji mogą być wstępnie obliczone (mam na myśli double x = pi * 1.5;i tym podobne). Jeśli kiedykolwiek zamierzasz używać PI w chrupiącej matematyce w ciasnych pętlach, lepiej upewnij się, że wartość jest znana kompilatorowi.
Eugene Ryabtsev,
7

Właśnie natknąłem tego artykułu przez Danny Kalev który ma wielką wskazówka dla C ++ 14 i do góry.

template<typename T>
constexpr T pi = T(3.1415926535897932385);

Pomyślałem, że to całkiem fajne (chociaż użyłbym tam najwyższej precyzji PI tam, gdzie mogłem), zwłaszcza, że ​​szablony mogą używać tego na podstawie typu.

template<typename T>
T circular_area(T r) {
  return pi<T> * r * r;
}
double darea= circular_area(5.5);//uses pi<double>
float farea= circular_area(5.5f);//uses pi<float>
Beta Jester
źródło
4

Wartości takie jak M_PI, M_PI_2, M_PI_4 itp. Nie są standardowym C ++, więc constexpr wydaje się lepszym rozwiązaniem. Można sformułować różne wyrażenia stałych, które obliczają to samo pi i dotyczy mnie to, czy (wszystkie) zapewniają mi pełną dokładność. Standard C ++ nie mówi wprost o tym, jak obliczyć pi. Dlatego zwykle wracam do ręcznego definiowania pi. Chciałbym podzielić się poniższym rozwiązaniem, które obsługuje wszystkie frakcje pi z pełną dokładnością.

#include <ratio>
#include <iostream>

template<typename RATIO>
constexpr double dpipart()
{
    long double const pi = 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899863;
    return static_cast<double>(pi * RATIO::num / RATIO::den);
}

int main()
{
    std::cout << dpipart<std::ratio<-1, 6>>() << std::endl;
}
Jeroen Lammertink
źródło
2
Bardzo dobrze. Może być konieczne, aby na końcu tej cyfry było „l” lub „L”. Otrzymuję ostrzeżenie o zwężeniu od mojego kompilatora gcc w systemie Linux.
Grant Rostig,
2

W systemie Windows (cygwin + g ++) uważam za konieczne dodanie flagi -D_XOPEN_SOURCE=500dla preprocesora w celu przetworzenia definicji M_PIw math.h.

tata Smerf
źródło
2
To nie jest odpowiedź, ale komentarz do odpowiedzi Fritzone.
0xbadf00d
2
@ 0xbadf00d: Jest to całkowicie samodzielna odpowiedź, która zawiera kroki niezbędne do M_PIrozpoczęcia pracy na konkretnej platformie. To już nie jest komentarz do odpowiedzi na jakąś inną platformę, ale odpowiedź na jakąś inną platformę jest komentarzem do tej.
Ben Voigt,
2

C ++ 14 pozwala na to static constexpr auto pi = acos(-1);

Willy Goat
źródło
9
std::acosnie jest constexpr. Twój kod się nie skompiluje.
0xbadf00d
@ 0xbadf00d Skompilowałem go za pomocą g ++
Willy Goat
12
@WillyGoat: Zatem g ++ jest błędny, ponieważ acosnie ma go constexprw C ++ 14 i nie proponuje się, aby stał się constexprnawet w C ++ 17
Ben Voigt
@BenVoigt czy są jakieś funkcje matematyczne constexpr? Najwyraźniej nie: stackoverflow.com/questions/17347935/constexpr-math-functions
wcochran
1
@wcochran: Istnieje wiele NOWYCH funkcji matematycznych constexpr, patrz na przykład ( github.com/kthohr/gcem ). Ale nie są one kompatybilne wstecz z funkcjami C o tej samej nazwie, więc nie mogą przejąć starych nazw.
Ben Voigt
2

Kilka eleganckich rozwiązań. Wątpię jednak, aby precyzja funkcji trygonometrycznych była równa precyzji typów. Dla tych, którzy wolą zapisywać stałą wartość, działa to dla g ++: -

template<class T>
class X {
public:
            static constexpr T PI = (T) 3.14159265358979323846264338327950288419\
71693993751058209749445923078164062862089986280348253421170679821480865132823066\
47093844609550582231725359408128481117450284102701938521105559644622948954930381\
964428810975665933446128475648233786783165271201909145648566923460;
...
}

Dokładność 256 cyfr dziesiętnych powinna wystarczyć dla każdego przyszłego długiego, długiego, podwójnego typu. Jeśli potrzebujesz więcej, odwiedź https://www.piday.org/million/ .

Jon Guiton
źródło
2
#include <cmath>
const long double pi = acos(-1.L);
Gjerich
źródło
1

Możesz to zrobić:

#include <cmath>
#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

Jeśli M_PIjest już zdefiniowane w cmath, nie zrobi nic innego niż dołączyć cmath. Jeśli M_PInie jest zdefiniowany (co ma miejsce na przykład w Visual Studio), to go zdefiniuje. W obu przypadkach możesz użyćM_PI aby uzyskać wartość pi.

Ta wartość pi pochodzi z qmath.h Qt Creatora.

Kaczor Donald
źródło
1

Możesz użyć tego:

#define _USE_MATH_DEFINES // for C++
#include <cmath>

#define _USE_MATH_DEFINES // for C
#include <math.h>

Stałe matematyczne nie są zdefiniowane w standardzie C / C ++. Aby ich użyć, musisz najpierw zdefiniować, _USE_MATH_DEFINESa następnie dołączyć cmathlub math.h.

Fazlı KUZU
źródło