Jest dość głupi problem z liczbą pi w C i C ++. O ile wiem, M_PI
definicja w math.h
nie jest wymagana przez żaden standard.
Nowe normy C ++ wprowadzono wiele skomplikowanych matematycznych w bibliotece standardowej - funkcje hiperboliczne, std::hermite
i std::cyl_bessel_i
, różnych generatorów liczb losowych i tak dalej i tak dalej.
Czy któryś z „nowych” standardów wprowadził stałą dla pi? Jeśli nie - dlaczego? Jak działa ta skomplikowana matematyka bez tego?
Zdaję sobie sprawę z podobnych pytań dotyczących pi w C ++ (mają kilka lat i są stare); Chciałbym poznać aktualny stan problemu.
Jestem również bardzo zainteresowany tym, dlaczego och, dlaczego C ++ nadal nie ma stałej pi, ale ma dużo bardziej skomplikowaną matematykę.
UPD: Wiem, że sam mogę zdefiniować pi jako 4 * atan (1) lub acos (1) lub double pi = 3,14. Pewnie. Ale dlaczego w 2018 roku nadal muszę to robić? Jak działają standardowe funkcje matematyczne bez pi?
UPD2: Zgodnie z tym raportem z podróży na posiedzenie komisji C ++ w lipcu 2019 r. W Kolonii, propozycja P0631 (stałe matematyczne) została zaakceptowana w C ++ 20. Wygląda więc na to, że nareszcie będziemy mieć numer pi w standardowej bibliotece!
Odpowiedzi:
Aż do C ++ 17 włącznie pi nie jest stałą wprowadzoną do języka i jest to utrapienie.
Mam to szczęście, że używam boost i definiują pi z wystarczająco dużą liczbą miejsc dziesiętnych nawet dla 128 bitów
long double
.Jeśli nie używasz funkcji Boost, zakoduj ją samodzielnie. Zdefiniowanie go za pomocą funkcji trygonometrycznej jest kuszące, ale jeśli to zrobisz, nie możesz tego zrobić
constexpr
. Dokładność funkcji trygonometrycznych nie jest również gwarantowana przez żaden znany mi standard ( por .std::sqrt
), Więc naprawdę znajdujesz się na niebezpiecznym gruncie, polegającym na takiej funkcji.Istnieje sposób na uzyskanie
constexpr
wartości pi za pomocą metaprogramowania: patrz http://timmurphy.org/2013/06/27/template-metaprogramming-in-c/Dobre wieści z C ++ 20. Nie jest defininition dla PI . C ++ 20 dodaje pewne stałe matematyczne w
<numbers>
. Na przykładstd::numbers::pi
jestdouble
typem.Źródła: https://en.cppreference.com/w/cpp/numeric/constants
źródło
constexpr
niestety, dlatego mówię „Definiowanie tego za pomocą funkcji trygonometrycznej jest bolesne”double
(lub jakiejś śmiesznej liczby, jeśli zależy Ci na hipotetycznych, naprawdę długich dubletach).fsin
instrukcji „około 1,37 tryliona jednostek na ostatnim miejscu, pozostawiając mniej niż cztery bity poprawne” . jeszcze gorzej dla dużych wejść, gdzie redukcja zakresu zawija się wielokrotnie. Jest to trochę styczne do stałych używanychlong double
w C ++, ale i tak zgrabne.Jak powiedzieli inni, nie ma,
std::pi
ale jeśli chcesz dokładnejPI
wartości, możesz użyć:Zakłada się, że implementacja C ++ generuje poprawnie zaokrągloną wartość PI from
acos(-1.0)
, która jest powszechna, ale nie jest gwarantowana .Tak nie jest
constexpr
, ale w praktyce optymalizacja kompilatorów, takich jak gcc i clang, ocenia je w czasie kompilacji. Deklarowania goconst
jest ważne dla optymalizator zrobić dobrą robotę, choć.źródło
acos()
funkcja ma nieskończone nachylenie przyx = -1
. W związku z tym metoda ta polega naacos()
implementacji, aby w zasadzie jawnie wychwycić wielkość liter dokładnego-1
argumentu i bezpośrednio zwrócić poprawną stałą. Lepiej użyj czegoś takiego,4*atan(1)
co jest matematycznie znacznie bardziej niezawodne (dobrze działające nachyleniex = 1
i mnożenie przez 4 jest zawsze dokładne w przypadku matematyki zmiennoprzecinkowej).std::acos
w wyrażeniu stałym. clang zgłasza to jako błąd. Prosimy zauważyć, że jest to niezgodne rozszerzenie i ostatecznie powinno zostać naprawione w gcc. Więcej informacji można znaleźć w tej odpowiedzi .Aż do C ++ 20 nie, żaden ze standardów nie wprowadzał stałej, która reprezentowałaby liczbę pi (π). Możesz przybliżyć liczbę w swoim kodzie:
Inne języki, takie jak C #, mają stałą zadeklarowaną w swoich bibliotekach.
Aktualizacja: Począwszy od C ++ 20, rzeczywiście
pi
w<numbers>
nagłówku jest zadeklarowana stała . Jest ona dostępna za pośrednictwem:std::numbers::pi
.źródło
inline
dla C ++ 17 +.double
. C # ma to łatwe, ponieważdouble
typ jest ustalony. Gdybym był w komitecie normalizacyjnym C ++, zaproponowałbym coś w stylustd::constants<double>::pi
std::numeric_limits<double>::is_iec559;
w takim przypadku powinieneś statycznie włączyć . Co, przyznaję, jest tym, co mam w moim „głównym nagłówku”. Zauważ, że formalnie musisz osobno sprawdzić wszystkie typy zmiennoprzecinkowe. To, że jeden jest IEEE754, nie oznacza, że wszyscy nim są.M_PI
jest definiowany przez „standard”, jeśli nie standard językowy : POSIX z rozszerzeniem X / Open System Interfaces (które jest bardzo często obsługiwane i wymagane do oficjalnego znakowania UNIX).Nie jest (nadal) pewne, co będzie w C ++ 20, ale skoro pytałeś: prawdopodobnie będzie miał takie stałe . Artykuł został scalony w ostatniej rundzie funkcji C ++ 20 (dla projektu komisji w sierpniu 2019).
W szczególności będzie istniał zarówno szablon
std::numbers::pi
(typudouble
), jak i zmienny, którego możesz użyć, jeśli chcesz mieć inny typ zmiennoprzecinkowy, npstd::numbers::pi_v<float>
. Pełną listę stałych można zobaczyć w [numbers.syn] .źródło
Nie jest to oczywiście dobry pomysł, ponieważ nie ma oczywistego typu definiującego pi, który byłby powszechnie stosowany w różnych domenach.
Liczba Pi jest oczywiście liczbą niewymierną, więc nie może być poprawnie reprezentowana przez żaden typ C ++. Możesz argumentować, że naturalnym podejściem jest zatem zdefiniowanie go w największym dostępnym typie zmiennoprzecinkowym. Jednak rozmiar największego standardowego typu zmiennoprzecinkowego
long double
nie jest zdefiniowany przez standard C ++, więc wartość stałej będzie się różnić w zależności od systemu. Co gorsza, dla każdego programu, w którym typ roboczy nie był największym typem, definicja pi byłaby niewłaściwa, ponieważ nakładałaby koszt wydajności przy każdym użyciu pi.Dla każdego programisty trywialne jest również znalezienie wartości pi i zdefiniowanie własnej odpowiedniej do użycia stałej, więc dołączenie jej do nagłówków matematycznych nie daje żadnej wielkiej korzyści.
źródło
pi
stałej polimorficznej jest jasną drogą do przodu - w języku z wnioskiem typu Hindley-Milner. W Haskell zawsze to robiliśmypi :: Floating a => a
, więcpi
automatycznie miałoby to wartość3.1415927
wFloat
kontekście,3.141592653589793
wDouble
kontekście iπ
w kontekście symboliczno-obliczeniowym. Ale czy ludzie naprawdę chcieliby jawnie utworzyć wystąpienie parametru szablonu? Wydaje się to trochę niewygodne, zwłaszcza jeśli stałalong double
implementacja dałaby identyczne wyniki w większości aplikacji.auto a = pi<float>;
jest całkowicie w porządku, z pewnością bardziej czytelne niż osławione4*atan(1)
Edytowano - aby usunąć termin niezbędny, ponieważ okazał się kontrowersyjny. To za dużo określenia absolutnego.
C ++ to duży i złożony język, z tego powodu Komitet Normalizacyjny zawiera tylko te rzeczy, które są bardzo potrzebne . Jak najwięcej pozostawiono standardowym bibliotekom niejęzycznym ... takim jak Boost.
boost :: math :: constants
źródło
std::hermite
istd::cyl_bessel_i
istd::cosh
istd::mersenne_twister_engine
istd::ranlux48
istd::cauchy_distribution
istd::assoc_laguerre
istd::beta
wszystkie były absolutnie konieczne, wszyscy używamy ich każdego dnia!