Verwendung der PI-Konstante in C ++

476

Ich möchte die PI-Konstante und die trigonometrischen Funktionen in einigen C ++ - Programmen verwenden. Ich bekomme die trigonometrischen Funktionen mit include <math.h>. Es scheint jedoch keine Definition für PI in dieser Header-Datei zu geben.

Wie kann ich PI erhalten, ohne es manuell zu definieren?

Etan
quelle
3
@tiwo, fragen Sie , was ist der Unterschied zwischen 3.14, 3.141592und atan(1) * 4?
Nikola Malešević
21
Als Randnotiz sollte cmath in C ++ anstelle von math.h verwendet werden, was für C.
juzzlin
4
Locker verwandt: Siehe cise.ufl.edu/~manuel/obfuscate/pi.c, wie der Wert von PI direkt aus der Definition berechnet wird.
Lorro
2
Es ist in C ++ 20 angekommen! stackoverflow.com/a/57285400/895245
Ciro Santilli 31 冠状 病 六四 事件 31

Antworten:

537

Auf einigen (insbesondere älteren) Plattformen (siehe Kommentare unten) müssen Sie dies möglicherweise tun

#define _USE_MATH_DEFINES

und fügen Sie dann die erforderliche Header-Datei hinzu:

#include <math.h>

und auf den Wert von pi kann zugegriffen werden über:

M_PI

In meinem math.h(2014) ist es definiert als:

# define M_PI           3.14159265358979323846  /* pi */

aber überprüfen Sie Ihre math.hfür mehr. Ein Auszug aus dem "Alten" math.h(2009):

/* 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.
 */

Jedoch:

  1. Auf neueren Plattformen (zumindest auf meinem 64-Bit-Ubuntu 14.04) muss ich das nicht definieren _USE_MATH_DEFINES

  2. Auf (neueren) Linux-Plattformen werden auch long doubleWerte als GNU-Erweiterung bereitgestellt:

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */
Ferenc Deak
quelle
51
#define _USE_MATH_DEFINESgefolgt von #include <math.h>Definitionen M_PIin Visual C ++. Vielen Dank.
Etan
3
Funktioniert auch mit Cygwin-Headern.
Rob
24
Sie können immer cmathanstelle von einschließen math.h.
Richard J. Ross III
10
Selbst nach der Definition, _USE_MATH_DEFINESob sich GCC beschwert, liegt dies daran, dass __STRICT_ANSI__definiert ist (vielleicht haben Sie bestanden -pedanticoder -std=c++11), was nicht M_PIdefiniert werden kann, und daher die Definition mit aufhebt -D__STRICT_ANSI__. Wenn Sie es selbst definieren, sollten Sie anstelle eines Makros C ++ verwenden constexpr auto M_PI = 3.14159265358979323846;.
Legends2k
1
Ab 2018 sollte die Antwort definitiv aktualisiert werden, um <cmath> anstelle von <math.h> zu verwenden
jaskmar
170

Pi kann berechnet werden als atan(1)*4. Sie können den Wert auf diese Weise berechnen und zwischenspeichern.

Konamiman
quelle
78
Für C ++ 11 Benutzer:constexpr double pi() { return std::atan(1)*4; }
Matiu
41
-1: Funktioniert nur wenn atan(1)*4 == 3.141592653589793238462643383279502884(grob gesagt). Ich würde nicht darauf wetten. Seien Sie normal und verwenden Sie ein Rohliteral, um die Konstante zu definieren. Warum Präzision verlieren, wenn Sie nicht müssen?
Thomas Eding
29
Man kann die Multiplikationsoperation mit vermeiden atan2(0, -1);.
Legends2k
44
@matiu atanist nicht constexpr.
R. Martinho Fernandes
45
Versuchen Sie acos(-1)stattdessen, keine Notwendigkeit für atan2.
user541686
113

Sie können auch Boost verwenden, das wichtige mathematische Konstanten mit maximaler Genauigkeit für den angeforderten Typ definiert (dh float vs double).

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

Weitere Beispiele finden Sie in der Boost-Dokumentation .

BuschnicK
quelle
184
Boost: Steigerung der bereits unnötigen Komplexität von C ++ seit 1999!
Dan Moulding
47
Eingängig und teilweise wahr. Auf der anderen Seite kann Boost manchmal phänomenal nützlich sein ...
BuschnicK
59
@ DanMoulding: Ähm. Ist C die einzige andere Sprache, die Sie kennen? Weil alle anderen Sprachen, die ich kenne, außer C, eine Standardbibliothek haben, die größer als C ++ ist (z. B. Python, Haskell, C #, PHP, Delphi, Erlang, Java, ......). Aus persönlicher Erfahrung ist diese elitäre not gonna use libsMeinung ein Schädling und wahrscheinlich der Hauptgrund für schlechte Software, die in C ++ geschrieben wurde.
Sebastian Mach
11
@Gracchus: Ja. C ++ ohne Bibliotheken (oder ohne die neuen C ++ 11-Bibliotheken) ist, so sehr ich diese Sprache mag und so sehr ich alles selbst codieren möchte, nicht sehr produktiv.
Sebastian Mach
14
Ich glaube, er sagte Komplexität nicht Größe . Vermutlich unter Bezugnahme auf a) die 3 verschachtelten Namespaces und b) die Definition von pi als Vorlagenfunktion und nicht nur als normale Konstante.
Timmmm
83

Holen Sie es sich stattdessen von der FPU-Einheit auf dem Chip:

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

double PI = get_PI();
Henrik
quelle
40
:-) wahrscheinlich nicht diese plattformunabhängige, aber eine schöne zusätzliche exotische lösung!
Etan
3
Ich liebe es, wie du hier aus der Box bist;)
VivienLeger
1
Ich liebe diese Antwort. Dies ist besonders nützlich, wenn Sie auf ältere x86-Plattformen abzielen. Dies ist eine kleine Modeerscheinung, bei der die optimierenden Compiler nicht so stark involviert sind wie moderne. Danke für diesen Henrik!
Matt
49

Ich würde empfehlen, nur pi mit der Genauigkeit einzugeben, die Sie benötigen. Dies würde Ihrer Ausführung keine Berechnungszeit hinzufügen und wäre portabel, ohne Header oder #defines zu verwenden. Die Berechnung von Acos oder Atan ist immer teurer als die Verwendung eines vorberechneten Wertes.

const double PI  =3.141592653589793238463;
const float  PI_F=3.14159265358979f;
Alex
quelle
28
Dies ist ein großartiges Beispiel, warum wir diesen Ansatz nicht wählen sollten, wir Menschen machen Fehler, runden, kopieren und einfügen usw. Ich denke, die Verwendung von M_PI ist der richtige Ansatz.
Nacho4d
10
Wenn Sie dies in C ++ 11 tun, machen Sie das consta constexpr.
Legends2k
3
@ nacho4d Ich bevorzuge auch M_PI, wenn es verfügbar ist, aber nicht alle Systeme sind POSIX-kompatibel. Ich denke, dieser Ansatz ist besser als die 4 * atan (1) -Methode für Fälle, in denen M_PI nicht verfügbar ist.
m24p
2
"Die Berechnung von Acos oder Atan ist immer teurer" ist nicht wahr. Jeder moderne Optimierungs-Compiler kennt sich mit Standard-Mathematikfunktionen aus und kann sich durch diese konstant verbreiten. Siehe zB goo.gl/BvdJyr
Nemo
2
@Nemo, Gegenbeispiel: godbolt.org/g/DsAern Wie bereits an anderer Stelle erwähnt, scheint dies derzeit nur bei GCC der Fall zu sein, und das liegt wahrscheinlich daran, dass die grundlegenden mathematischen Funktionen als deklariert wurden constexpr.
Parker Coates
47

Anstatt zu schreiben

#define _USE_MATH_DEFINES

Ich würde empfehlen, -D_USE_MATH_DEFINESoder /D_USE_MATH_DEFINESabhängig von Ihrem Compiler.

Auf diese Weise können Sie sicher sein, dass Sie selbst im Falle einer Person, die den Header vor Ihnen enthält (und ohne #define), die Konstanten anstelle eines obskuren Compilerfehlers haben, dessen Aufspüren Sie ewig brauchen werden.

Matthieu M.
quelle
Guter Tipp. Wenn "Sie" eine Kompilierungseinheit sind, können Sie natürlich sicherstellen, dass das Makro definiert ist, bevor etwas enthalten ist. Wenn "Sie" jedoch eine Header-Datei sind, liegt dies außerhalb Ihrer Kontrolle.
Steve Jessop
3
In der Tat, auch wenn "Sie" eine Kompilierungseinheit sind ... abhängig von der Reihenfolge der Header ist der kürzeste Weg zum Wartungsalptraum ...
Matthieu M.
1
Sie müssen sich jedoch nicht auf die Reihenfolge der Header verlassen. Es spielt keine Rolle, ob sich die Header gegenseitig einschließen, vorausgesetzt, Sie führen die #Definition durch, bevor Sie # überhaupt etwas einschließen (zumindest unter der Annahme, dass nichts #undefs). Gleiches gilt für NDEBUG.
Steve Jessop
1
Das häufigste Problem in einem Projekt ist, dass Sie beim Kompilieren mit Visual Studio beispielsweise nicht wissen, in welcher Reihenfolge der Compiler Ihre Dateien durchsucht. Wenn Sie sie also <cmath>an verschiedenen Orten verwenden, wird dies zu einem großen Problem (insbesondere wenn es in einer anderen Bibliothek enthalten ist, die Sie einschließen). Es wäre viel besser gewesen, wenn sie diesen Teil außerhalb der Kopfschutzvorrichtungen platziert hätten, aber jetzt können sie nicht viel dagegen tun. Die Compiler-Direktive funktioniert in der Tat ziemlich gut.
Meneldal
40

Da die offizielle Standardbibliothek keinen konstanten PI definiert, müssten Sie ihn selbst definieren. Die Antwort auf Ihre Frage "Wie kann ich PI erhalten, ohne es manuell zu definieren?" lautet "Sie tun es nicht - oder Sie verlassen sich auf einige compilerspezifische Erweiterungen." Wenn Sie sich keine Gedanken über die Portabilität machen, können Sie dies im Handbuch Ihres Compilers nachlesen.

Mit C ++ können Sie schreiben

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

Es ist jedoch nicht garantiert, dass die Initialisierung dieser Konstante statisch ist. Der G ++ - Compiler behandelt diese mathematischen Funktionen jedoch als intrinsisch und kann diesen konstanten Ausdruck zur Kompilierungszeit berechnen.

sellibitze
quelle
6
Normalerweise verwende ich acos (-1), wie Sie sagen, sie werden zur Kompilierungszeit ausgewertet. Als ich M_PI, acos (-1) und atan (1) * 4 getestet habe, habe ich identische Werte erhalten.
Micah
2
Die traditionelle Art ist zu verwenden 4*atan(1.): atanist einfach zu implementieren und das Multiplizieren mit 4 ist eine exakte Operation. Natürlich falten moderne Compiler alle Konstanten mit der erforderlichen Präzision (zielen darauf ab, sie zu falten), und es ist durchaus sinnvoll, sie zu verwenden, acos(-1)oder sogar std::abs(std::arg(std::complex<double>(-1.,0.)))das Gegenteil von Eulers Formel und damit ästhetischer, als es scheint (ich habe hinzugefügt, absweil ich es nicht tue). Ich erinnere mich nicht, wie die komplexe Ebene geschnitten wird oder ob dies überhaupt definiert ist.
tobi_s
Nur damit niemand versehentlich denkt, dass Sie es ernst meinen (wieder -_- '). Dies ist eine schreckliche Lösung. Die atan-Implementierung ist nicht durch den Standard definiert, dh seine Implementierung, und möglicherweise hw-abhängig. Dies bedeutet, dass die Zahlen schrecklich sein können, was bedeutet, dass Sie mit 3.14 im Allgemeinen möglicherweise besser dran sind. Weiter ist es möglicherweise langsam, auch für die Sonderfälle.
Midjji
32

Aus der Posix-Manpage von 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
quelle
3
Gute Antwort, aber der Link ist tot. Ich schlage vor , diese eine Stelle.
Abderrahim Kitouni
30

C ++ 20 std::numbers::pi

Endlich ist es soweit: http://eel.is/c++draft/numbers

Ich erwarte, dass die Verwendung wie folgt ist:

#include <numbers>
#include <iostream>

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

Ich werde es versuchen, wenn der Support bei GCC eintrifft. GCC 9.1.0 g++-9 -std=c++2aunterstützt ihn immer noch nicht.

Der akzeptierte Vorschlag beschreibt:

5.0. "Header" [Header] In der Tabelle [tab: cpp.library.headers] muss ein neuer <math>Header hinzugefügt werden.

[...]

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

Es gibt std::numbers::enatürlich auch eine :-) Wie berechnet man die Eulerkonstante oder Euler in C ++?

Diese Konstanten verwenden die C ++ 14-Variablenvorlagenfunktion: C ++ 14-Variablenvorlagen: Wozu dienen sie? Irgendein Verwendungsbeispiel?

In früheren Versionen des Entwurfs befand sich die Konstante unter std::math::pi: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
27

Standard C ++ hat keine Konstante für PI.

Viele C ++ - Compiler definieren M_PIin cmath(oder in math.hfür C) eine nicht standardmäßige Erweiterung. Möglicherweise müssen Sie, #define _USE_MATH_DEFINESbevor Sie es sehen können.

RichieHindle
quelle
18

Ich würde es tun

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

oder

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

Ich würde nicht auf die Genauigkeit in π eingeben Sie benötigen . Was soll das überhaupt bedeuten? Die Präzision, die Sie benötigen, ist die Präzision von T, aber wir wissen nichts darüberT .

Sie könnten sagen: Worüber reden Sie? Twird sein float, doubleoder long double. Geben Sie also einfach die Genauigkeit von ein long double, z

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

Aber wissen Sie wirklich, dass es in Zukunft keinen neuen Gleitkommatyp im Standard geben wird, der noch präziser ist als long double ? Das tust du nicht.

Und deshalb ist die erste Lösung schön. Sie können ziemlich sicher sein, dass der Standard die trigonometrischen Funktionen für einen neuen Typ überladen würde.

Und bitte sagen Sie nicht, dass die Auswertung einer trigonometrischen Funktion bei der Initialisierung eine Leistungsbeeinträchtigung darstellt.

0xbadf00d
quelle
1
Beachten Sie das arg(log(x)) == πfür alle 0 < x < 1.
0xbadf00d
Das ist eine schreckliche Idee. Verwenden Sie eine pro Typ überladene Vorlage constexpr. Auf diese Weise erhalten Sie einen Kompilierungsfehler, der Sie zwingt, ihn zu definieren, wenn ein neuer Typ angezeigt wird. Es ist auch im Allgemeinen schrecklich, weil die Triggertypen nicht auf Gleitkommatypen beschränkt sind. Genießen Sie also den Fehler atan (1) ... Der Standard garantiert nicht, dass trigonometrische Funktionen ihre tatsächlichen trigonometrischen Werte mit der Genauigkeit des Typs berechnen. Sie tun dies im Allgemeinen nicht, und es wird schlimmer mit z. B. Fastmath und ist immer besonders schlecht für die besonderen Werte.
Midjji
10

Ich verwende Folgendes in einem meiner gemeinsamen Header im Projekt, das alle Grundlagen abdeckt:

#define _USE_MATH_DEFINES
#include <cmath>

#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

#ifndef M_PIl
#define M_PIl (3.14159265358979323846264338327950288)
#endif

Nebenbei bemerkt definieren alle unten aufgeführten Compiler die Konstanten M_PI und M_PIl, wenn Sie sie einschließen <cmath>. Es ist nicht erforderlich, `#define _USE_MATH_DEFINES hinzuzufügen, was nur für VC ++ erforderlich ist.

x86 GCC 4.4+
ARM GCC 4.5+
x86 Clang 3.0+
Shital Shah
quelle
Kann der Downvoter kommentieren, was mit dieser Antwort falsch ist? Dies ist gut erforscht und getestet und wird in realen Systemen verwendet. Ich wollte es definitiv verbessern, wenn etwas nicht stimmt.
Shital Shah
1
Zu Ihrer Information, Borland C ++ - Compiler definieren auch M_PIohne Notwendigkeit_USE_MATH_DEFINES
Remy Lebeau
8

Im Allgemeinen definiere ich lieber meine eigenen: const double PI = 2*acos(0.0);weil nicht alle Implementierungen sie für Sie bereitstellen.

Die Frage, ob diese Funktion zur Laufzeit aufgerufen oder zur Kompilierungszeit statisch ausgegeben wird, ist normalerweise kein Problem, da sie ohnehin nur einmal auftritt.

Sumudu Fernando
quelle
8
acos (-1) ist auch pi.
Roderick Taylor
3
Das Laden eines Sofortoperanden erfordert häufig weniger CPU-Anweisungen und / oder weniger Latenz als das Lesen eines Operanden von einem Speicherort. Außerdem konnten nur Ausdrücke vorberechnet werden, die zur Kompilierungszeit bekannt sind (ich meine double x = pi * 1.5;und dergleichen). Wenn Sie jemals beabsichtigen, PI in knackigen Berechnungen in engen Schleifen zu verwenden, sollten Sie sicherstellen, dass der Wert dem Compiler bekannt ist.
Eugene Ryabtsev
7

Ich bin gerade auf diesen Artikel von Danny Kalev gestoßen, der einen großartigen Tipp für C ++ 14 und höher enthält.

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

Ich fand das ziemlich cool (obwohl ich dort den PI mit der höchsten Präzision verwenden würde), vor allem, weil Vorlagen ihn basierend auf dem Typ verwenden können.

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
quelle
4

Werte wie M_PI, M_PI_2, M_PI_4 usw. sind kein Standard-C ++, daher scheint ein constexpr eine bessere Lösung zu sein. Es können verschiedene const-Ausdrücke formuliert werden, die den gleichen pi berechnen, und es geht mich an, ob sie (alle) mir die volle Genauigkeit bieten. Der C ++ - Standard erwähnt nicht explizit, wie pi berechnet wird. Daher neige ich dazu, pi manuell zu definieren. Ich möchte die folgende Lösung teilen, die alle Arten von Brüchen von pi mit voller Genauigkeit unterstützt.

#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
quelle
2
Sehr schön. Möglicherweise muss am Ende dieser Zahl ein "l" oder "L" stehen. Ich erhalte eine Einschränkung von meinem Compiler gcc unter Linux.
Grant Rostig
2

Unter Windows (cygwin + g ++) musste das Flag -D_XOPEN_SOURCE=500für den Präprozessor hinzugefügt werden , um die Definition von M_PIin zu verarbeiten math.h.

Papa Schlumpf
quelle
2
Dies ist keine Antwort, sondern ein Kommentar zu Fritzones Antwort.
0xbadf00d
2
@ 0xbadf00d: Dies ist eine vollständig eigenständige Antwort, die die Schritte enthält, die erforderlich sind, um M_PIauf einer bestimmten Plattform zu arbeiten. Das ist kein Kommentar zu einer Antwort für eine andere Plattform mehr, sondern eine Antwort für eine andere Plattform ist ein Kommentar zu dieser.
Ben Voigt
2

Mit C ++ 14 können Sie dies tun static constexpr auto pi = acos(-1);

Willy Goat
quelle
9
std::acosist nicht ein constexpr. Ihr Code wird also nicht kompiliert.
0xbadf00d
@ 0xbadf00d Ich habe es mit g ++ kompiliert
Willy Goat
12
@WillyGoat: Dann ist g ++ falsch, weil acoses nicht constexprin C ++ 14 ist und nicht vorgeschlagen wird, constexprauch in C ++ 17 zu werden
Ben Voigt
@ BenVoigt gibt es irgendwelche mathematischen Funktionen, die sind constexpr? Anscheinend nicht: stackoverflow.com/questions/17347935/constexpr-math-functions
wcochran
1
@wcochran: Es gibt viele NEUE mathematische Funktionen constexpr, siehe zum Beispiel ( github.com/kthohr/gcem ). Sie sind jedoch nicht abwärtskompatibel mit den gleichnamigen C-Funktionen, sodass sie die alten Namen nicht übernehmen können.
Ben Voigt
2

Einige elegante Lösungen. Ich bin mir nicht sicher, ob die Genauigkeit der trigonometrischen Funktionen der Genauigkeit der Typen entspricht. Für diejenigen, die lieber einen konstanten Wert schreiben, funktioniert dies für g ++: -

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

Die Genauigkeit von 256 Dezimalstellen sollte für jeden zukünftigen Long Long Long Double-Typ ausreichen. Wenn weitere erforderlich sind, besuchen Sie https://www.piday.org/million/ .

Jon Guiton
quelle
2
#include <cmath>
const long double pi = acos(-1.L);
gjerich
quelle
1

Du kannst das:

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

Wenn dies M_PIbereits in definiert ist cmath, wird nichts anderes als Include ausgeführt cmath. Wenn M_PIes nicht definiert ist (was beispielsweise in Visual Studio der Fall ist), wird es definiert. In beiden Fällen können Sie verwendenM_PI den Wert von pi abrufen.

Dieser Wert von pi stammt aus Qmath.h von Qt Creator.

Donald Duck
quelle
1

Sie können das verwenden:

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

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

Mathematische Konstanten sind in Standard C / C ++ nicht definiert. Um sie zu verwenden, müssen Sie zuerst definieren _USE_MATH_DEFINESund dann schließen cmathoder math.h.

Fazlı KUZU
quelle