Czy C ++ 11, 14, 17 lub 20 wprowadza standardową stałą dla pi?

170

Jest dość głupi problem z liczbą pi w C i C ++. O ile wiem, M_PIdefinicja w math.hnie jest wymagana przez żaden standard.

Nowe normy C ++ wprowadzono wiele skomplikowanych matematycznych w bibliotece standardowej - funkcje hiperboliczne, std::hermitei 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!

Amomum
źródło
Zauważasz istnienie starych pytań, takich jak stała pi niezależna od platformy? . Jeśli martwisz się, że są nieaktualne, zawsze możesz wyznaczyć nagrodę za jednego z nich, prosząc o odpowiedzi w oparciu o C ++ 17 itp ... Wtedy wszystkie odpowiedzi byłyby w jednym miejscu. Dlaczego to wciąż dobre pytanie, ale być może powinno skupić się na tym, dlaczego, a proszenie o aktualne powinno być nagrodą w przypadku istniejących pytań.
Shafik Yaghmour
Myślę, że warto dodać nowe odpowiedzi, ponieważ C ++ 20 dodał stałą pi, o ile wiem
Guillaume Racicot
@GuillaumeRacicot Zaktualizowałem pytanie. Nie jestem pewien, czy powinniśmy zająć się C ++ 20, ponieważ nie jest jeszcze oficjalnie opublikowany.
Amomum
@GuillaumeRacicot: Trochę za późno na dodanie jednego…
Davis Herring

Odpowiedzi:

101

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 constexprwartoś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ład std::numbers::pijest doubletypem.

Źródła: https://en.cppreference.com/w/cpp/numeric/constants

Batszeba
źródło
9
@Lundin: Ale to nie może być constexprniestety, dlatego mówię „Definiowanie tego za pomocą funkcji trygonometrycznej jest bolesne”
Batszeba
9
Dlaczego to ma znaczenie? Pi jest stałą, niepodlegającą zmianom ani niczego specyficznego dla implementacji. Po prostu wypisz wartość (w obiekcie const, jeśli chcesz) do liczby miejsc istotnych dla double(lub jakiejś śmiesznej liczby, jeśli zależy Ci na hipotetycznych, naprawdę długich dubletach).
R .. GitHub STOP HELPING ICE
10
@ R .. Problem, który mam z tym jest taki, że to, co wydaje się teraz absurdalne, może stać się zupełnie rozsądne za mniej więcej 20 lat. (Przypomina mi się 640k Billa Gatesa). Ufam, że Boost nadąża za ewolucją architektury.
Batszeba
10
@KonradRudolph: Wysoka precyzja pi ma znaczenie przy wdrażaniu redukcji zasięgu. Na przykład wewnętrzna stała Pi x86 / x87 (pełna mantysa 64-bitowa) prowadzi do błędu w najgorszym przypadku dla małych danych wejściowych do fsininstrukcji „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żywanych long doublew C ++, ale i tak zgrabne.
Peter Cordes,
31

Jak powiedzieli inni, nie ma, std::piale jeśli chcesz dokładnej PIwartości, możesz użyć:

constexpr double pi = std::acos(-1);

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 go constjest ważne dla optymalizator zrobić dobrą robotę, choć.

0x476f72616e
źródło
2
Jest to niebezpieczne, ponieważ acos()funkcja ma nieskończone nachylenie przy x = -1. W związku z tym metoda ta polega na acos()implementacji, aby w zasadzie jawnie wychwycić wielkość liter dokładnego -1argumentu 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 nachylenie x = 1i mnożenie przez 4 jest zawsze dokładne w przypadku matematyki zmiennoprzecinkowej).
cmaster
1
Nie wolno nam używać std::acosw 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 .
badola
31

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:

constexpr double pi = 3.14159265358979323846;

Inne języki, takie jak C #, mają stałą zadeklarowaną w swoich bibliotekach.

Aktualizacja: Począwszy od C ++ 20, rzeczywiście piw <numbers>nagłówku jest zadeklarowana stała . Jest ona dostępna za pośrednictwem: std::numbers::pi.

Ron
źródło
6
Możesz chcieć dodać inlinedla C ++ 17 +.
Deduplicator
8
Ta. Głosuj za, ale pamiętaj, że Twoja definicja jest nadal podatna na nieprecyzyjność w przypadku platform z różnymi definicjami domen double. C # ma to łatwe, ponieważ doubletyp jest ustalony. Gdybym był w komitecie normalizacyjnym C ++, zaproponowałbym coś w stylustd::constants<double>::pi
Batszeba,
11
@Deduplicator nie jest również implicite constexpr wbudowanym ...?
kiedy
4
@R. Wystarczająco uczciwe, chociaż 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ą.
Batszeba
2
@DanielSchepler Więc czym ma być ta „liczba”? Nie wiedziałem, że istnieją liczby podwójne o podstawie 16.
BЈовић
29

M_PIjest 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(typu double), jak i zmienny, którego możesz użyć, jeśli chcesz mieć inny typ zmiennoprzecinkowy, np std::numbers::pi_v<float>. Pełną listę stałych można zobaczyć w [numbers.syn] .

Davis Herring
źródło
Zapraszam do przeformułowania mojej edycji według własnego uznania - chciałem tylko dodać rzeczywistą pisownię nowych stałych tutaj dla potomności.
Barry
12

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

Jack Aidley
źródło
10
C ++ 14 dał nam szablony zmiennych. Czy nie po to są?
Amomum
21
mówić jak prawdziwy członek komitetu, mówić o wszystkich sposobach, w jakie nie można tego zrobić pomimo przedstawionej jasnej ścieżki rozwoju. Zobacz zaskakująco odpowiedni przykład szablonów zmiennych . Nie sądzę, że twoja odpowiedź jest zła, ale jestem pewien, że nie pomogłaby ona w wiecznej depresji Bjarne Stroustrup z powodu żalu przekazania kontroli nad przyszłością C ++ bardzo niezdecydowanej komisji.
kiedy
4
@snb Zgadzam się, że tworzenie pistałej polimorficznej jest jasną drogą do przodu - w języku z wnioskiem typu Hindley-Milner. W Haskell zawsze to robiliśmy pi :: Floating a => a, więc piautomatycznie miałoby to wartość 3.1415927w Floatkontekście, 3.141592653589793w Doublekontekś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ła long doubleimplementacja dałaby identyczne wyniki w większości aplikacji.
leftaroundokoło
2
@leftaroundabout Uważam, że pisanie auto a = pi<float>;jest całkowicie w porządku, z pewnością bardziej czytelne niż osławione4*atan(1)
Amomum
1
Ugh, przegłosowałem to przez błędne kliknięcie i teraz nie mogę tego cofnąć. Przepraszam. To zasługuje na +1 za całkiem dobre wyjaśnienie uzasadnienia komisji, dlaczego nie zostało dodane, nawet jeśli osobiście uważam, że rozumowanie jest zasadniczo błędne z powodów, które ludzie już wskazali.
Załóż pozew Moniki
-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

Tiger4Hire
źródło
29
Jasne, std::hermitei std::cyl_bessel_ii std::coshi std::mersenne_twister_enginei std::ranlux48i std::cauchy_distributioni std::assoc_laguerrei std::betawszystkie były absolutnie konieczne, wszyscy używamy ich każdego dnia!
Amomum
2
Jestem prawie pewien, że nie można było skłonić nawet samej komisji do zaakceptowania idei, że wszystko, na co głosują, jest „absolutnie konieczne”. Nie chodzi o konieczność, ale o dodawanie wartości do języka - prawie nic w standardowej bibliotece nie jest potrzebne do pisania programów, a do tego większość podstawowego języka można również wyrzucić (jeśli nie masz nic przeciwko pracy w To znaczy Turinga tarpit). Wartość włączenia stałej dla pi można dyskutować, ale idea, że ​​jej tam nie ma, ponieważ nie jest konieczna, nie jest ważna.
Jeroen Mostert
Nie narzekaj na mnie, po prostu cytuję komitet normalizacyjny. Od dawna toczyły się otwarte dyskusje na temat tego, ile wzmocnienia powinno być zawarte w standardzie C ++ 11. Powodem, dla którego powstał tak mały podzbiór, jest to, że autorzy kompilatorów narzekali na ilość testów. Dlatego jeśli coś jest w normach, to jest tam, ponieważ ktoś uznał za konieczne ujednolicenie tego. To, że nie wiesz dlaczego, nie oznacza, że ​​nie było powodu.
Tiger4Hire
1
@ Tiger4Hire Jestem pewien, że wszystko ma powód, po prostu nie mogę zrozumieć, dlaczego stała dla pi nie została dodana, gdy było dużo bardziej złożonych rzeczy. Stała jest łatwa do napisania za pomocą szablonów zmiennych i nie wymaga wielu testów od twórców kompilatorów.
Amomum
@Amomum: Tak, dodanie pi wydaje się niewielkim kosztem dla dużej wygranej. Uwaga, osobiście wolałbym zobaczyć std :: network przed std :: math :: constants.
Tiger4Hire