Come usare la costante PI in C ++


476

Voglio usare la costante PI e le funzioni trigonometriche in alcuni programmi C ++. Ottengo le funzioni trigonometriche con include <math.h>. Tuttavia, non sembra esserci una definizione per PI in questo file di intestazione.

Come posso ottenere PI senza definirlo manualmente?


3
@tiwo, stai chiedendo qual è la differenza tra 3.14, 3.141592e atan(1) * 4?
Nikola Malešević,

21
Come nota a margine, cmath dovrebbe essere usato in C ++ invece di math.h, che è per C.
juzzlin

4
Liberamente correlato: vedere cise.ufl.edu/~manuel/obfuscate/pi.c su come calcolare il valore di PI direttamente dalla definizione.
Lorro,

Risposte:


537

Su alcune piattaforme (soprattutto vecchie) (vedi i commenti sotto) potresti doverlo fare

#define _USE_MATH_DEFINES

e quindi includere il file di intestazione necessario:

#include <math.h>

e il valore di pi è accessibile tramite:

M_PI

Nel mio math.h(2014) è definito come:

# define M_PI           3.14159265358979323846  /* pi */

ma controlla il tuo math.hper di più. Un estratto dal "vecchio" math.h(nel 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.
 */

Però:

  1. su piattaforme più recenti (almeno sul mio Ubuntu 14.04 a 64 bit) non ho bisogno di definire il _USE_MATH_DEFINES

  2. Sulle (recenti) piattaforme Linux ci sono long doubleanche valori forniti come estensione GNU:

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */

51
#define _USE_MATH_DEFINESseguito da #include <math.h>definisce M_PIin Visual c ++. Grazie.
Etan,

3
Funziona anche con le intestazioni cygwin.
Rob

24
Puoi sempre includere cmathinvece di math.h.
Richard J. Ross III,

10
Anche dopo aver definito _USE_MATH_DEFINESse GCC si lamenta che è perché __STRICT_ANSI__è definito (forse hai superato -pedantico -std=c++11) che M_PInon consente di essere definito, quindi non definirlo -D__STRICT_ANSI__. Quando lo definisci tu stesso, dal momento che è C ++, invece di una macro dovresti constexpr auto M_PI = 3.14159265358979323846;.
legends2k

1
A partire dal 2018, la risposta dovrebbe sicuramente essere aggiornata per utilizzare <cmath> anziché <math.h>
jaskmar

170

Pi può essere calcolato come atan(1)*4. È possibile calcolare il valore in questo modo e memorizzarlo nella cache.


78
Per gli utenti di c ++ 11:constexpr double pi() { return std::atan(1)*4; }
matiu,

41
-1: funziona solo se atan(1)*4 == 3.141592653589793238462643383279502884(approssimativamente parlando). Non ci scommetterei. Sii normale e usa un valore letterale grezzo per definire la costante. Perché perdere precisione quando non è necessario?
Thomas Eding,

29
Si può evitare l'operazione di moltiplicazione con atan2(0, -1);.
legends2k

44
@matiu atanno constexpr.
R. Martinho Fernandes,

45
Prova acos(-1)invece, non è necessario atan2.
user541686

113

È inoltre possibile utilizzare boost, che definisce importanti costanti matematiche con la massima precisione per il tipo richiesto (ovvero float vs double).

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

Consulta la documentazione di boost per ulteriori esempi.


184
Boost: Potenziamento della già inutile complessità del C ++ dal 1999!
Dan Molding,

47
Accattivante e in parte vero. D'altra parte il potenziamento può essere
straordinariamente

59
@DanMoulding: Uhm. C è l'unica altra lingua che conosci? Perché tutti gli altri linguaggi che conosco, tranne C, hanno una libreria standard che è di dimensioni maggiori di C ++ '(ad esempio Python, Haskell, C #, PHP, Delphi, Erlang, Java, ......). Per esperienza personale, not gonna use libsquell'opinione elitaria è un parassita e probabilmente la ragione numero uno per il cattivo software scritto in C ++.
Sebastian Mach,

11
@Gracco: Sì. Il C ++ senza librerie (o senza le nuove librerie C ++ 11) è, per quanto mi piaccia quella lingua e quanto vorrei codificare tutto da solo, non molto produttivo.
Sebastian Mach,

14
Credo che abbia detto complessità non dimensioni . Presumibilmente riferendosi a a) i 3 spazi dei nomi nidificati eb) definendo pi come una funzione basata su modelli anziché come una costante normale.
Timmmm,

83

Prendi invece dall'unità FPU su chip:

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

double PI = get_PI();

40
:-) probabilmente non quella piattaforma indipendente, ma una bella soluzione esotica aggiuntiva!
Etan,

3
adoro come hai
pensato

1
Adoro questa risposta. È particolarmente utile quando si prendono di mira piattaforme x86 più vecchie, che è una piccola moda negli ultimi tempi in cui i compilatori di ottimizzazione non sono così terribilmente coinvolti come quelli moderni. Grazie per questo Henrik!
Matt,

49

Consiglierei di digitare pi per la precisione di cui hai bisogno. Ciò non aggiungerebbe alcun tempo di calcolo alla tua esecuzione e sarebbe portatile senza usare intestazioni o #define. Il calcolo di acos o atan è sempre più costoso rispetto all'utilizzo di un valore precalcolato.

const double PI  =3.141592653589793238463;
const float  PI_F=3.14159265358979f;

28
Questo è un ottimo esempio del perché non dovremmo adottare questo approccio, noi persone commettiamo errori, arrotondamenti, copia e incolla, ecc. Penso che usare M_PI sia l'approccio giusto.
nacho4d

10
Se uno lo sta facendo in C ++ 11, crea consta constexpr.
legends2k

3
@ nacho4d Anche io preferisco M_PI se è disponibile, ma non tutti i sistemi sono conformi a POSIX. Penso che questo approccio sia migliore del metodo 4 * atan (1) per i casi in cui M_PI non è disponibile.
m24p,

2
"Il calcolo di acos o atan è sempre più costoso" non è vero. Qualsiasi moderno compilatore di ottimizzazione sa tutto sulle funzioni matematiche standard e può propagarsi costantemente attraverso di esse. Vedi ad esempio goo.gl/BvdJyr
Nemo il

2
@Nemo, contro esempio: godbolt.org/g/DsAern Come è stato detto altrove, sembra che solo GCC lo faccia attualmente e questo è probabilmente perché ha dichiarato le funzioni matematiche di base come constexpr.
Parker Coates,

47

Piuttosto che scrivere

#define _USE_MATH_DEFINES

Consiglierei di utilizzare -D_USE_MATH_DEFINESo in /D_USE_MATH_DEFINESbase al compilatore.

In questo modo ti viene assicurato che anche nel caso in cui qualcuno includa l'intestazione prima di te (e senza #define) avrai comunque le costanti invece di un oscuro errore del compilatore che impiegherai anni per rintracciare.


Buona dritta. Se "tu" sei un'unità di compilazione, ovviamente puoi assicurarti che la macro sia definita prima di includere qualsiasi cosa. Ma se "tu" sei un file header, è fuori dal tuo controllo.
Steve Jessop,

3
In effetti anche se "tu" sei un'unità di compilazione ... a seconda dell'ordine delle intestazioni è il percorso più breve verso l'incubo della manutenzione ...
Matthieu M.

1
Tuttavia, non è necessario dipendere dall'ordinamento delle intestazioni. Non importa se le intestazioni si includano a vicenda, a condizione che tu faccia #define prima di includere # niente affatto (almeno, supponendo che nulla lo comprenda). Lo stesso vale per NDEBUG.
Steve Jessop,

1
Il problema molto comune in un progetto è che se stai compilando Visual Studio, ad esempio, non sai in quale ordine il compilatore esaminerà i tuoi file, quindi se lo usi <cmath>in luoghi diversi diventa un grosso problema (specialmente se è incluso da un'altra libreria che stai includendo). Sarebbe stato molto meglio se avessero messo quella parte al di fuori delle protezioni del colpo di testa, ma bene non possono fare molto al riguardo ora. La direttiva del compilatore funziona davvero abbastanza bene.
meneldal

40

Poiché la libreria standard ufficiale non definisce un PI costante, dovresti definirlo tu stesso. Quindi la risposta alla tua domanda "Come posso ottenere PI senza definirlo manualmente?" è "No - o fai affidamento su alcune estensioni specifiche del compilatore.". Se non sei preoccupato per la portabilità, puoi consultare questo manuale del compilatore.

C ++ ti permette di scrivere

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

ma l'inizializzazione di questa costante non è garantita come statica. Il compilatore G ++ tuttavia gestisce quelle funzioni matematiche come intrinseche ed è in grado di calcolare questa espressione costante in fase di compilazione.


6
Di solito uso acos (-1), come dici tu, sono valutati in fase di compilazione. Quando ho testato M_PI, acos (-1) e atan (1) * 4, ho ottenuto valori identici.
Michea,

2
Il modo tradizionale è usare 4*atan(1.): atanè facile da implementare e moltiplicare per 4 è un'operazione esatta. Naturalmente, i compilatori moderni piegano (mirano a piegare) tutte le costanti con la precisione richiesta, ed è perfettamente ragionevole usare acos(-1)o anche std::abs(std::arg(std::complex<double>(-1.,0.)))qual è l'inverso della formula di Eulero e quindi più esteticamente piacevole di quanto sembri (ho aggiunto absperché non ricorda come viene tagliato il piano complesso o se questo è definito).
tobi_s,

Solo così nessuno pensa accidentalmente che tu sia serio (di nuovo -_- '). Questa è una soluzione terribile. L'implementazione atan non è definita dallo standard che significa la sua implementazione e possibilmente dipendente da hw. Ciò significa che i numeri possono essere terribili, il che significa che potresti stare meglio usando 3.14 in generale. Inoltre è molto probabilmente lento, anche per i casi speciali.
midjji,

32

Dalla pagina man Posix di 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

3
Buona risposta ma il link è morto. Suggerisco questo posto.
Abderrahim Kitouni,

30

C ++ 20 std::numbers::pi

Finalmente è arrivato: http://eel.is/c++draft/numbers

Mi aspetto che l'utilizzo sia simile a:

#include <numbers>
#include <iostream>

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

Ci proverò quando il supporto arriva a GCC, GCC 9.1.0 con g++-9 -std=c++2aancora non lo supporta.

La proposta accettata descrive:

5.0. "Headers" [headers] Nella tabella [tab: cpp.library.headers], è <math>necessario aggiungere una nuova intestazione.

[...]

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

C'è std::numbers::eovviamente anche un :-) Come calcolare la costante di Eulero o Eulero alimentato in C ++?

Queste costanti utilizzano la funzione del modello di variabile C ++ 14: Modelli di variabili C ++ 14: qual è il loro scopo? Qualche esempio di utilizzo?

Nelle versioni precedenti del progetto, la costante era sotto std::math::pi: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf


27

Il C ++ standard non ha una costante per PI.

Molti compilatori C ++ definiscono M_PIin cmath(o in math.hper C) un'estensione non standard. Potrebbe essere necessario #define _USE_MATH_DEFINESprima di poterlo vedere.


18

farei

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

o

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

Vorrei non digitando π per la precisione è necessario . Cosa dovrebbe anche significare? La precisione di cui hai bisogno è la precisione di T, ma non sappiamo nulla T.

Potresti dire: di cosa stai parlando? Tsarà float, doubleo long double. Quindi, basta digitare la precisione di long double, ad es

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

Ma sai davvero che in futuro non ci sarà un nuovo tipo a virgola mobile nello standard con una precisione ancora maggiore di long double? Non

Ed è per questo che la prima soluzione è bellissima. Puoi essere certo che lo standard sovraccaricherebbe le funzioni trigonometriche per un nuovo tipo.

E per favore, non dire che la valutazione di una funzione trigonometrica durante l'inizializzazione è una penalità di prestazione.


1
Nota che arg(log(x)) == πper tutti 0 < x < 1.
0xbadf00d,

Questa è un'idea terribile. usa un modello sovraccarico per tipo constexpr, in questo modo ottieni un errore di compilazione per forzarti a definirlo se appare un nuovo tipo. È anche generalmente terribile perché i tipi di trigge non si limitano ai tipi a virgola mobile. Quindi goditi l'errore atan (1) ... Lo standard non garantisce che le funzioni trigonometriche calcolino i loro valori trigonometrici effettivi con la precisione del tipo. Generalmente non lo fanno, e peggiora ad es. Con la matematica ed è sempre particolarmente dannoso per i valori speciali.
Midjji,

10

Uso il seguente in una delle mie intestazioni comuni nel progetto che copre tutte le basi:

#define _USE_MATH_DEFINES
#include <cmath>

#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

#ifndef M_PIl
#define M_PIl (3.14159265358979323846264338327950288)
#endif

Su una nota a margine, tutti i compilatori seguenti definiscono le costanti M_PI e M_PIl se si include <cmath>. Non è necessario aggiungere `#define _USE_MATH_DEFINES che è richiesto solo per VC ++.

x86 GCC 4.4+
ARM GCC 4.5+
x86 Clang 3.0+

Il downvoter può commentare cosa c'è di sbagliato in questa risposta. Questo è ben studiato e testato e in uso nel sistema reale. Mi piacerebbe sicuramente migliorarlo se qualcosa non va.
Shital Shah,

1
Cordiali saluti, compilatori C ++ Borland definiscono anche M_PIsenza bisogno_USE_MATH_DEFINES
Remy Lebeau

8

In genere preferisco definire il mio: const double PI = 2*acos(0.0);perché non tutte le implementazioni te lo forniscono.

La domanda se questa funzione viene chiamata in fase di esecuzione o se è statica disattivata al momento della compilazione non è in genere un problema, perché si verifica comunque solo una volta.


8
acos (-1) è anche pi.
Roderick Taylor,

3
Spesso è necessario meno istruzioni della CPU e / o minore latenza per caricare un operando immediato rispetto alla lettura di un operando da una posizione di memoria. Inoltre, solo le espressioni conosciute in fase di compilazione potrebbero essere pre-calcolate (intendo double x = pi * 1.5;e simili). Se hai mai intenzione di usare PI in matematica croccante in loop stretti, assicurati che il valore sia noto al compilatore.
Eugene Ryabtsev,

7

Ho appena trovato questo articolo di Danny Kalev che ha un ottimo consiglio per C ++ 14 e versioni successive.

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

Ho pensato che fosse abbastanza bello (anche se avrei usato il PI di massima precisione possibile), soprattutto perché i modelli possono usarlo in base al tipo.

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>

4

Valori come M_PI, M_PI_2, M_PI_4, ecc. Non sono C ++ standard, quindi un constexpr sembra una soluzione migliore. Esistono diverse espressioni const che calcolano lo stesso pi e mi preoccupa se (tutti) mi forniscono la massima precisione. Lo standard C ++ non menziona esplicitamente come calcolare pi. Pertanto, tendo a definire pi manualmente. Vorrei condividere la soluzione di seguito che supporta tutti i tipi di frazioni di pi con la massima precisione.

#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;
}

2
Molto bella. Potrebbe essere necessario avere una "l" o "L" alla fine di quel numero. Ricevo un avvertimento restrittivo dal mio compilatore gcc su linux.
Concedi Rostig il

2

Su windows (cygwin + g ++), ho trovato necessario aggiungere il flag -D_XOPEN_SOURCE=500per il preprocessore per elaborare la definizione di M_PIin math.h.


2
Questa non è una risposta, ma un commento alla risposta di Fritzone.
0xbadf00d,

2
@ 0xbadf00d: è una risposta completamente autonoma che fornisce i passaggi necessari per M_PIlavorare su una particolare piattaforma. Questo non è più un commento su una risposta per qualche altra piattaforma che una risposta per un'altra piattaforma è un commento su questa.
Ben Voigt,

2

C ++ 14 ti consente di farlo static constexpr auto pi = acos(-1);


9
std::acosnon è un constexpr. Quindi, il tuo codice non verrà compilato.
0xbadf00d,

@ 0xbadf00d L'ho compilato con g ++
Willy Goat

12
@WillyGoat: Quindi g ++ è sbagliato, perché acosnon è constexprin C ++ 14, e non si propone di diventare constexprnemmeno in C ++ 17
Ben Voigt,

@BenVoigt ci sono delle funzioni matematiche constexpr? A quanto pare no: stackoverflow.com/questions/17347935/constexpr-math-functions
wcochran

1
@wcochran: ci sono molte NUOVE funzioni matematiche constexpr, vedi ad esempio ( github.com/kthohr/gcem ). Ma non sono retrocompatibili con le funzioni C con lo stesso nome, quindi non possono assumere i vecchi nomi.
Ben Voigt,

2

Alcune soluzioni eleganti. Dubito tuttavia che la precisione delle funzioni trigonometriche sia uguale alla precisione dei tipi. Per quelli che preferiscono scrivere un valore costante, questo funziona per g ++: -

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

La precisione di 256 cifre decimali dovrebbe essere sufficiente per qualsiasi tipo doppio futuro lungo lungo lungo. Se sono necessari altri, visitare https://www.piday.org/million/ .


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

1

Puoi farlo:

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

Se M_PIè già definito in cmath, questo non farà altro che includere cmath. Se M_PInon è definito (come nel caso di Visual Studio), lo definirà. In entrambi i casi, è possibile utilizzare M_PIper ottenere il valore di pi.

Questo valore di pi deriva dal qmath.h di Qt Creator.


1

Puoi usarlo:

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

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

Le costanti matematiche non sono definite in Standard C / C ++. Per usarli, devi prima definire _USE_MATH_DEFINESe poi includere cmatho math.h.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.