Comment utiliser la constante PI en C ++

476

Je veux utiliser la constante PI et les fonctions trigonométriques dans certains programmes C ++. J'obtiens les fonctions trigonométriques avec include <math.h>. Cependant, il ne semble pas y avoir de définition de PI dans ce fichier d'en-tête.

Comment puis-je obtenir PI sans le définir manuellement?

Etan
la source
3
@tiwo, demandez-vous quelle est la différence entre 3.14, 3.141592et atan(1) * 4?
Nikola Malešević
21
En remarque, cmath doit être utilisé en C ++ au lieu de math.h, qui est pour C.
juzzlin
4
Librement lié: voir cise.ufl.edu/~manuel/obfuscate/pi.c sur la façon de calculer la valeur de PI directement à partir de la définition.
lorro
3
Il est arrivé en C ++ 20! stackoverflow.com/a/57285400/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

537

Sur certaines plateformes (en particulier les plus anciennes) (voir les commentaires ci-dessous), vous devrez peut-être

#define _USE_MATH_DEFINES

puis inclure le fichier d'en-tête nécessaire:

#include <math.h>

et la valeur de pi est accessible via:

M_PI

Dans mon math.h(2014), il est défini comme:

# define M_PI           3.14159265358979323846  /* pi */

mais vérifiez votre math.hpour plus. Un extrait de "l'ancien" math.h(en 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.
 */

Toutefois:

  1. sur les plates-formes plus récentes (au moins sur mon Ubuntu 64.04 64 bits), je n'ai pas besoin de définir _USE_MATH_DEFINES

  2. Sur les plates-formes Linux (récentes), des long doublevaleurs sont également fournies en tant qu'extension GNU:

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */
Ferenc Deak
la source
51
#define _USE_MATH_DEFINESsuivi de #include <math.h>define M_PIen visual c ++. Merci.
Etan
3
Fonctionne également avec les en-têtes cygwin.
Rob
24
Vous pouvez toujours inclure cmathau lieu de math.h.
Richard J. Ross III
10
Même après avoir défini _USE_MATH_DEFINESsi GCC se plaint, c'est parce que __STRICT_ANSI__est défini (peut-être que vous avez réussi -pedanticou -std=c++11) qui interdit M_PId'être défini, donc indéfinissez-le avec -D__STRICT_ANSI__. Lorsque vous le définissez vous-même, puisque c'est C ++, au lieu d'une macro, vous devriez constexpr auto M_PI = 3.14159265358979323846;.
legends2k
1
À partir de 2018, la réponse devrait être définitivement mise à jour pour utiliser <cmath> au lieu de <math.h>
jaskmar
170

Pi peut être calculé comme atan(1)*4. Vous pouvez calculer la valeur de cette façon et la mettre en cache.

Konamiman
la source
78
Pour les utilisateurs de C ++ 11:constexpr double pi() { return std::atan(1)*4; }
matiu
41
-1: ne fonctionne que si atan(1)*4 == 3.141592653589793238462643383279502884(grosso modo). Je ne parierais pas dessus. Soyez normal et utilisez un littéral brut pour définir la constante. Pourquoi perdre de la précision lorsque vous n'en avez pas besoin?
Thomas Eding
29
On peut éviter l'opération de multiplication avec atan2(0, -1);.
legends2k
44
@matiu atanne l'est pas constexpr.
R. Martinho Fernandes
45
Essayez acos(-1)plutôt, pas besoin atan2.
user541686
113

Vous pouvez également utiliser boost, qui définit des constantes mathématiques importantes avec une précision maximale pour le type demandé (c'est-à-dire float vs double).

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

Consultez la documentation de boost pour plus d'exemples.

BuschnicK
la source
184
Boost: Booster la complexité déjà inutile de C ++ depuis 1999!
Dan Moulding du
47
Accrocheur et en partie vrai. D'un autre côté, le boost peut être
incroyablement
59
@DanMoulding: Uhm. C est-il la seule autre langue que vous connaissez? Parce que tous les autres langages que je connais, sauf C, ont une bibliothèque standard dont les amplitudes sont supérieures à C ++ '(par exemple Python, Haskell, C #, PHP, Delphi, Erlang, Java, ......). Par expérience personnelle, cette not gonna use libsopinion élitiste est un ravageur et probablement la principale raison des mauvais logiciels écrits en C ++.
Sebastian Mach
11
@Gracchus: Ouaip. C ++ sans bibliothèques (ou sans les nouvelles bibliothèques C ++ 11) est, autant que j'aime ce langage et autant que je voudrais tout coder moi-même, pas très productif.
Sebastian Mach
14
Je pense qu'il a dit complexité et non taille . Se référant vraisemblablement à a) les 3 espaces de noms imbriqués, et b) définissant pi comme une fonction de modèle plutôt que juste une constante normale.
Timmmm
83

Obtenez-le de l'unité FPU sur puce à la place:

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

double PI = get_PI();
Henrik
la source
40
:-) probablement pas indépendant de cette plateforme, mais une belle solution exotique supplémentaire!
Etan
3
j'adore comment vous sortez de la boîte ici;)
VivienLeger
1
J'adore cette réponse. Il est particulièrement utile lorsque vous ciblez des plates-formes x86 plus anciennes, ce qui est un peu à la mode ces derniers temps où les compilateurs d'optimisation ne sont pas aussi terriblement impliqués que les modernes. Merci pour ce Henrik!
Matt
49

Je recommanderais simplement de taper pi avec la précision dont vous avez besoin. Cela n'ajouterait aucun temps de calcul à votre exécution et serait portable sans utiliser d'en-têtes ou #defines. Le calcul d'acos ou d'atan est toujours plus cher que l'utilisation d'une valeur précalculée.

const double PI  =3.141592653589793238463;
const float  PI_F=3.14159265358979f;
Alex
la source
28
C'est un excellent exemple pourquoi nous ne devrions pas adopter cette approche, nous les gens faisons des erreurs, arrondissons, copions et collons, etc. Je pense que l'utilisation de M_PI est la bonne approche.
nacho4d
10
Si l'on fait cela en C ++ 11, faites le consta constexpr.
legends2k
3
@ nacho4d Je préfère moi aussi M_PI s'il est disponible, mais tous les systèmes ne sont pas compatibles POSIX. Je pense que cette approche est meilleure que la méthode 4 * atan (1) pour les cas où M_PI n'est pas disponible.
m24p
2
"Calculer des acos ou des atans coûte toujours plus cher" n'est pas vrai. Tout compilateur d'optimisation moderne sait tout sur les fonctions mathématiques standard et peut se propager en permanence à travers elles. Voir par exemple goo.gl/BvdJyr
Nemo
2
@Nemo, exemple de compteur: godbolt.org/g/DsAern Comme cela a été dit ailleurs, il semble que seul GCC le fasse actuellement et c'est probablement parce qu'il a déclaré les fonctions mathématiques de base comme constexpr.
Parker Coates
47

Plutôt que d'écrire

#define _USE_MATH_DEFINES

Je recommanderais d'utiliser -D_USE_MATH_DEFINESou en /D_USE_MATH_DEFINESfonction de votre compilateur.

De cette façon, vous êtes assuré que même dans le cas où quelqu'un inclurait l'en-tête avant vous (et sans le #define), vous aurez toujours les constantes au lieu d'une erreur de compilation obscure que vous prendrez beaucoup de temps à rechercher.

Matthieu M.
la source
Bon conseil. Si «vous» êtes une unité de compilation, vous pouvez bien sûr vous assurer que la macro est définie avant d'inclure quoi que ce soit. Mais si "vous" êtes un fichier d'en-tête, c'est hors de votre contrôle.
Steve Jessop
3
En fait même si "vous" êtes une unité de compilation ... dépendre de l'ordre des en-têtes est le chemin le plus court vers un cauchemar de maintenance ...
Matthieu M.
1
Cependant, vous ne devez pas dépendre de l'ordre des en-têtes. Peu importe que les en-têtes s'incluent mutuellement, à condition que vous fassiez le #define avant de #inclure quoi que ce soit (du moins, en supposant que rien # ne le défende). Il en va de même pour NDEBUG.
Steve Jessop
1
Le problème très courant dans un projet est que si vous compilez avec Visual Studio par exemple, vous ne savez pas dans quel ordre le compilateur va parcourir vos fichiers, donc si vous utilisez <cmath>à différents endroits, cela devient très difficile (en particulier s'il est inclus par une autre bibliothèque que vous incluez). Cela aurait été bien mieux s'ils mettaient cette partie en dehors des protège-têtes, mais ils ne peuvent pas faire grand-chose maintenant. La directive du compilateur fonctionne assez bien en effet.
meneldal
40

Étant donné que la bibliothèque standard officielle ne définit pas un PI constant, vous devez le définir vous-même. Donc, la réponse à votre question "Comment puis-je obtenir PI sans le définir manuellement?" est "Vous ne le faites pas - ou vous comptez sur certaines extensions spécifiques au compilateur.". Si vous n'êtes pas préoccupé par la portabilité, vous pouvez consulter le manuel de votre compilateur pour cela.

C ++ vous permet d'écrire

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

mais l'initialisation de cette constante n'est pas garantie d'être statique. Le compilateur G ++ gère cependant ces fonctions mathématiques comme intrinsèques et est capable de calculer cette expression constante au moment de la compilation.

sellibitze
la source
6
J'utilise généralement des acos (-1), comme vous le dites, ils sont évalués au moment de la compilation. Lorsque j'ai testé M_PI, acos (-1) et atan (1) * 4, j'ai obtenu des valeurs identiques.
Micah
2
La manière traditionnelle est d'utiliser 4*atan(1.): atanest facile à mettre en œuvre et la multiplication par 4 est une opération exacte. Bien sûr, les compilateurs modernes plient (visent à plier) toutes les constantes avec la précision requise, et il est parfaitement raisonnable d'utiliser acos(-1)ou même std::abs(std::arg(std::complex<double>(-1.,0.)))qui est l'inverse de la formule d'Euler et donc plus esthétique qu'il n'y paraît (j'ai ajouté absparce que je ne ' t rappelez-vous comment le plan complexe est coupé ou s'il est défini du tout).
tobi_s
Juste pour que personne ne pense accidentellement que vous êtes sérieux (encore -_- '). C'est une terrible solution. L'implémentation atan n'est pas définie par la norme, c'est-à-dire son implémentation et éventuellement dépendante du matériel. Cela signifie que les chiffres peuvent être terribles, ce qui signifie que vous pourriez mieux utiliser 3.14 en général. De plus, il est possible que ce soit lent, même pour les cas particuliers.
midjji
32

Depuis la page de manuel Posix de 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
la source
3
Bonne réponse mais le lien est mort. Je suggère celui-ci à la place.
Abderrahim Kitouni
30

C ++ 20 std::numbers::pi

Enfin, il est arrivé: http://eel.is/c++draft/numbers

Je m'attends à ce que l'utilisation soit comme:

#include <numbers>
#include <iostream>

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

Je vais essayer quand le support arrivera à GCC, GCC 9.1.0 g++-9 -std=c++2ane le supporte toujours pas.

La proposition acceptée décrit:

5.0. «En-têtes» [en-têtes] Dans le tableau [tab: cpp.library.headers], un nouvel en- <math>tête doit être ajouté.

[...]

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

Il y a aussi std::numbers::ebien sûr :-) Comment calculer la constante d'Euler ou Euler propulsé en C ++?

Ces constantes utilisent la fonction de modèle de variable C ++ 14: Modèles de variable C ++ 14: quel est leur objectif? Un exemple d'utilisation?

Dans les versions précédentes du projet, la constante était sous std::math::pi: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf

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

Le C ++ standard n'a pas de constante pour PI.

De nombreux compilateurs C ++ définissent M_PIdans cmath(ou dans math.hpour C) une extension non standard. Vous devrez peut-être #define _USE_MATH_DEFINESavant de pouvoir le voir.

RichieHindle
la source
18

je ferais

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

ou

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

Je ne taperais pas π avec la précision dont vous avez besoin . Qu'est-ce que cela est même censé signifier? La précision dont vous avez besoin est la précision de T, mais nous ne savons rien T.

Vous pourriez dire: de quoi parlez-vous? Tsera float, doubleou long double. Donc, tapez simplement la précision de long double, c.-à-d.

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

Mais savez-vous vraiment qu'il n'y aura pas de nouveau type à virgule flottante dans la norme à l'avenir avec une précision encore plus élevée que long double? Non.

Et c'est pourquoi la première solution est belle. Vous pouvez être sûr que la norme surchargerait les fonctions trigonométriques pour un nouveau type.

Et s'il vous plaît, ne dites pas que l'évaluation d'une fonction trigonométrique à l'initialisation est une pénalité de performance.

0xbadf00d
la source
1
Notez que arg(log(x)) == πpour tous 0 < x < 1.
0xbadf00d
C'est une terrible idée. utilisez un modèle constexpr surchargé par type, de cette façon vous obtenez une erreur de compilation pour vous forcer à le définir si un nouveau type apparaît. C'est aussi généralement terrible, car les types de trig ne sont pas limités aux types à virgule flottante. Alors profitez de l'erreur atan (1) ... La norme ne garantit pas que les fonctions trigonométriques calculent leurs valeurs trigonométriques réelles avec la précision du type. Ils ne le font généralement pas, et cela empire avec fastmath par exemple et est toujours particulièrement mauvais pour les valeurs spéciales.
midjji
10

J'utilise le suivi dans l'un de mes en-têtes communs dans le projet qui couvre toutes les bases:

#define _USE_MATH_DEFINES
#include <cmath>

#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

#ifndef M_PIl
#define M_PIl (3.14159265358979323846264338327950288)
#endif

En passant, tous les compilateurs ci-dessous définissent les constantes M_PI et M_PIl si vous les incluez <cmath>. Il n'est pas nécessaire d'ajouter `#define _USE_MATH_DEFINES qui n'est requis que pour VC ++.

x86 GCC 4.4+
ARM GCC 4.5+
x86 Clang 3.0+
Shital Shah
la source
Le votant peut-il commenter ce qui ne va pas avec cette réponse? Ceci est bien recherché et testé et est utilisé dans un système réel. J'aurais certainement aimé l'améliorer si quelque chose ne va pas.
Shital Shah
1
Pour info, les compilateurs Borland C ++ définissent également M_PIsans avoir besoin_USE_MATH_DEFINES
Remy Lebeau
8

Je préfère généralement définir la mienne: const double PI = 2*acos(0.0);car toutes les implémentations ne vous la fournissent pas.

La question de savoir si cette fonction est appelée au moment de l'exécution ou est statique au moment de la compilation n'est généralement pas un problème, car elle ne se produit qu'une seule fois de toute façon.

Sumudu Fernando
la source
8
acos (-1) est également pi.
Roderick Taylor
3
Il faut souvent moins d'instructions CPU et / ou moins de latence pour charger un opérande immédiat que de lire un opérande à partir d'un emplacement mémoire. De plus, seules les expressions connues au moment de la compilation peuvent être précalculées (je veux dire double x = pi * 1.5;, etc.). Si vous avez l'intention d'utiliser PI en mathématiques croquantes dans des boucles serrées, vous feriez mieux de vous assurer que la valeur est connue du compilateur.
Eugene Ryabtsev
7

Je viens de tomber sur cet article de Danny Kalev qui a une excellente astuce pour C ++ 14 et plus.

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

Je pensais que c'était assez cool (même si j'utiliserais le PI le plus précis possible), surtout parce que les modèles peuvent l'utiliser en fonction du type.

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
la source
4

Des valeurs comme M_PI, M_PI_2, M_PI_4, etc. ne sont pas du C ++ standard, donc un constexpr semble une meilleure solution. Différentes expressions const peuvent être formulées qui calculent le même pi et je me demande si elles (toutes) me fournissent la précision totale. Le standard C ++ ne mentionne pas explicitement comment calculer pi. Par conséquent, j'ai tendance à revenir à la définition de pi manuellement. Je voudrais partager la solution ci-dessous qui prend en charge toutes sortes de fractions de pi en toute précision.

#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
la source
2
Très agréable. Il peut être nécessaire d'avoir un «l» ou un «L» à la fin de ce nombre. Je reçois un avertissement de rétrécissement de mon compilateur gcc sur linux.
Grant Rostig
2

Sur Windows (cygwin + g ++), j'ai trouvé nécessaire d'ajouter le drapeau -D_XOPEN_SOURCE=500du préprocesseur pour traiter la définition de M_PIin math.h.

grand Schtroumpf
la source
2
Ce n'est pas une réponse, mais un commentaire à la réponse de Fritzone.
0xbadf00d
2
@ 0xbadf00d: C'est une réponse complètement autonome qui fournit les étapes nécessaires pour commencer à M_PItravailler sur une plate-forme particulière. Ce n'est pas un commentaire sur une réponse pour une autre plate-forme, pas plus qu'une réponse pour une autre plate-forme n'est un commentaire sur celle-ci.
Ben Voigt
2

C ++ 14 vous permet de faire static constexpr auto pi = acos(-1);

Chèvre Willy
la source
9
std::acosn'est pas un constexpr. Donc, votre code ne se compilera pas.
0xbadf00d
@ 0xbadf00d Je l'ai compilé avec g ++
Willy Goat
12
@WillyGoat: Alors g ++ est faux, car il acosn'est pas constexpren C ++ 14, et n'est pas proposé de devenir constexprmême en C ++ 17
Ben Voigt
@BenVoigt y a-t-il des fonctions mathématiques qui le sont constexpr? Apparemment pas: stackoverflow.com/questions/17347935/constexpr-math-functions
wcochran
1
@wcochran: Il y a beaucoup de NOUVELLES fonctions mathématiques qui le sont constexpr, voir par exemple ( github.com/kthohr/gcem ). Mais ils ne sont pas rétrocompatibles avec les fonctions C du même nom, ils ne peuvent donc pas reprendre les anciens noms.
Ben Voigt
2

Quelques solutions élégantes. Je doute cependant que la précision des fonctions trigonométriques soit égale à la précision des types. Pour ceux qui préfèrent écrire une valeur constante, cela fonctionne pour g ++: -

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

La précision de 256 chiffres décimaux devrait suffire pour tout type double long long long long. Si d'autres sont nécessaires, visitez https://www.piday.org/million/ .

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

Tu peux le faire:

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

Si M_PIest déjà défini dans cmath, cela ne fera rien d'autre que d'inclure cmath. S'il M_PIn'est pas défini (ce qui est le cas par exemple dans Visual Studio), il le définira. Dans les deux cas, vous pouvez utiliser M_PIpour obtenir la valeur de pi.

Cette valeur de pi provient du qmath.h de Qt Creator.

Donald Duck
la source
1

Vous pouvez utiliser cela:

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

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

Les constantes mathématiques ne sont pas définies dans le standard C / C ++. Pour les utiliser, vous devez d'abord définir _USE_MATH_DEFINESpuis inclure cmathou math.h.

Fazlı KUZU
la source