Como usar a constante PI em C ++

476

Eu quero usar as funções constantes e trigonométricas do PI em algum programa C ++. Eu recebo as funções trigonométricas com include <math.h>. No entanto, não parece haver uma definição para PI neste arquivo de cabeçalho.

Como posso obter o PI sem defini-lo manualmente?

Etan
fonte
3
@tiwo, você está perguntando qual é a diferença entre 3.14, 3.141592e atan(1) * 4?
Nikola Malešević
21
Como uma nota lado, cmath deve ser usado em C ++, em vez de math.h, que é para C.
juzzlin
4
Relacionado livremente : consulte cise.ufl.edu/~manuel/obfuscate/pi.c sobre como calcular o valor do PI diretamente da definição.
lorro 26/07
2
Chegou em C ++ 20! stackoverflow.com/a/57285400/895245
Ciro Santilli escreveu:

Respostas:

537

Em algumas plataformas (especialmente mais antigas) (veja os comentários abaixo), pode ser necessário

#define _USE_MATH_DEFINES

e inclua o arquivo de cabeçalho necessário:

#include <math.h>

e o valor de pi pode ser acessado via:

M_PI

No meu math.h(2014), é definido como:

# define M_PI           3.14159265358979323846  /* pi */

mas verifique o seu math.hpara mais. Um extrato do "antigo" math.h(em 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.
 */

Contudo:

  1. em plataformas mais novas (pelo menos no meu Ubuntu 14.04 de 64 bits), não preciso definir o _USE_MATH_DEFINES

  2. Nas plataformas Linux (recentes), existem long doublevalores também fornecidos como uma Extensão GNU:

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */
Ferenc Deak
fonte
51
#define _USE_MATH_DEFINESseguido por #include <math.h>define M_PIno visual c ++. Obrigado.
Etan
3
Também funciona com cabeçalhos cygwin.
Rob
24
Você sempre pode incluir em cmathvez de math.h.
Richard J. Ross III
10
Mesmo depois de definir _USE_MATH_DEFINESse o GCC reclama, é porque __STRICT_ANSI__está definido (talvez você tenha passado -pedanticou -std=c++11) que não M_PIpermite ser definido, portanto, o defina -D__STRICT_ANSI__. Ao definir você mesmo, já que é C ++, em vez de uma macro, você deve constexpr auto M_PI = 3.14159265358979323846;.
precisa saber é o seguinte
1
A partir de 2018, a resposta deve definitivamente ser atualizados para usar <cmath> em vez de <math.h>
jaskmar
170

Pi pode ser calculado como atan(1)*4. Você pode calcular o valor dessa maneira e armazená-lo em cache.

Konamiman
fonte
78
Para usuários do c ++ 11:constexpr double pi() { return std::atan(1)*4; }
matiu
41
-1: Funciona apenas se atan(1)*4 == 3.141592653589793238462643383279502884(grosso modo). Eu não apostaria nisso. Seja normal e use um literal bruto para definir a constante. Por que perder precisão quando você não precisa?
Thomas Eding
29
Pode-se evitar a operação de multiplicação com atan2(0, -1);.
Legends2k
44
@matiu atannão é constexpr.
R. Martinho Fernandes
45
Tente em acos(-1)vez disso, não há necessidade atan2.
user541686
113

Você também pode usar o boost, que define constantes matemáticas importantes com a máxima precisão para o tipo solicitado (por exemplo, float vs double).

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

Confira a documentação do impulso para mais exemplos.

BuschnicK
fonte
184
Boost: aumentando a complexidade já desnecessária do C ++ desde 1999!
Dan Molding
47
Cativante e parcialmente verdadeiro. Por outro impulso mão pode ser fenomenal útil às vezes ...
BuschnicK
59
@ DanMoulding: Uhm. C é o único outro idioma que você conhece? Porque todas as outras linguagens que conheço, exceto C, têm uma biblioteca padrão que é maior que C ++ '(por exemplo, Python, Haskell, C #, PHP, Delphi, Erlang, Java, ......). Por experiência pessoal, esse elitista not gonna use libs-opinion é uma praga e provavelmente a principal razão para um software ruim escrito em C ++.
Sebastian Mach
11
@Gracchus: Sim. C ++ sem bibliotecas (ou sem as novas bibliotecas C ++ 11) é, tanto quanto eu gosto dessa linguagem e quanto eu gostaria de codificar tudo sozinho, não muito produtivo.
Sebastian Mach
14
Eu acredito que ele disse complexidade, não tamanho . Presumivelmente, referindo-se a) aos 3 espaços para nome aninhados eb) definindo pi como uma função de modelo em vez de apenas uma constante normal.
precisa saber é
83

Obtê-lo da unidade FPU no chip:

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

double PI = get_PI();
Henrik
fonte
40
:-) provavelmente não é independente dessa plataforma, mas uma boa solução exótica adicional!
Etan 4/15
3
eu amo como você
pensa
1
Eu amo essa resposta. É particularmente útil ao direcionar plataformas x86 mais antigas, que é uma pequena moda nos últimos tempos, onde os compiladores de otimização não estão tão terrivelmente envolvidos quanto os modernos. Obrigado por este Henrik!
Matt
49

Eu recomendaria apenas digitar pi com a precisão que você precisa. Isso não adicionaria tempo de cálculo à sua execução e seria portátil sem o uso de cabeçalhos ou #defines. Calcular acos ou atan é sempre mais caro do que usar um valor pré-calculado.

const double PI  =3.141592653589793238463;
const float  PI_F=3.14159265358979f;
Alex
fonte
28
Este é um ótimo exemplo de por que não devemos adotar essa abordagem, nós cometemos erros, arredondamos, copiamos e colamos etc. Acho que usar o M_PI é a abordagem correta.
nacho4d
10
Se alguém estiver fazendo isso no C ++ 11, faça o consta constexpr.
precisa saber é o seguinte
3
@ nacho4d Eu também prefiro o M_PI, se estiver disponível, mas nem todos os sistemas são compatíveis com POSIX. Eu acho que essa abordagem é melhor que o método 4 * atan (1) para os casos em que o M_PI não está disponível.
M24p
2
"Calcular acos ou atan é sempre mais caro" não é verdade. Qualquer compilador de otimização moderno conhece tudo sobre funções matemáticas padrão e pode se propagar constantemente através delas. Veja, por exemplo, goo.gl/BvdJyr
Nemo
2
@ Nem, exemplo contrário: godbolt.org/g/DsAern Como já foi dito em outros lugares, parece que apenas o GCC faz isso atualmente e é provável que tenha declarado as funções matemáticas básicas constexpr.
Parker Coates
47

Em vez de escrever

#define _USE_MATH_DEFINES

Eu recomendaria usar -D_USE_MATH_DEFINESou /D_USE_MATH_DEFINESdependendo do seu compilador.

Dessa forma, você tem certeza de que, mesmo no caso de alguém incluir o cabeçalho antes de você (e sem o #define), você ainda terá as constantes, em vez de um erro obscuro do compilador, que levará anos para rastrear.

Matthieu M.
fonte
Boa dica. Se "você" é uma unidade de compilação, é claro que você pode garantir que a macro seja definida antes que qualquer coisa seja incluída. Mas se "você" é um arquivo de cabeçalho, está fora de seu controle.
13139 Steve Jessop
3
Na verdade, mesmo que "você" é uma unidade de compilação ... dependendo da ordenação dos cabeçalhos é um caminho mais curto em direção pesadelo de manutenção ...
Matthieu M.
1
Você não precisa depender da ordem dos cabeçalhos. Não importa se os cabeçalhos se incluem, desde que você faça o #define antes de incluir qualquer coisa (pelo menos, supondo que nada #define). O mesmo se aplica ao NDEBUG.
Steve Jessop
1
O problema muito comum em um projeto é que, se você está compilando com o Visual Studio, por exemplo, não sabe em que ordem o compilador analisará seus arquivos; portanto, se você usá-lo <cmath>em locais diferentes, isso será uma grande dor (especialmente se estiver incluído em outra biblioteca que você está incluindo). Teria sido muito melhor se eles colocassem essa parte fora dos protetores de cabeçalho, mas agora não podem fazer muito sobre isso agora. A diretiva do compilador funciona muito bem mesmo.
Meneldal 18/05
40

Como a biblioteca padrão oficial não define um PI constante, você precisa defini-lo. Portanto, a resposta para sua pergunta "Como posso obter o PI sem defini-lo manualmente?" é "Você não precisa - ou confia em algumas extensões específicas do compilador.". Se você não está preocupado com a portabilidade, consulte o manual do seu compilador.

C ++ permite escrever

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

mas não é garantido que a inicialização dessa constante seja estática. No entanto, o compilador G ++ lida com essas funções matemáticas como intrínsecas e é capaz de calcular essa expressão constante em tempo de compilação.

sellibitze
fonte
6
Eu costumo usar acos (-1), como você diz, eles são avaliados em tempo de compilação. Quando testei M_PI, acos (-1) e atan (1) * 4, obtive valores idênticos.
Micah
2
A maneira tradicional é usar 4*atan(1.): atané fácil de implementar e multiplicar por 4 é uma operação exata. É claro que os compiladores modernos dobram (visam dobrar) todas as constantes com a precisão necessária, e é perfeitamente razoável usar acos(-1)ou mesmo std::abs(std::arg(std::complex<double>(-1.,0.)))qual é o inverso da fórmula de Euler e, portanto, mais esteticamente agradável do que parece (adicionei absporque não lembre-se de como o plano complexo é cortado ou se é definido).
tobi_s
Só assim ninguém acidentalmente pensa que você está falando sério (de novo -_- '). Esta é uma solução terrível. A implementação atan não é definida pelo padrão, significando sua implementação e possivelmente dependente de hw. Isso significa que os números podem ser terríveis, o que significa que é melhor você usar o 3.14 em geral. Além disso, é bem lento, mesmo para casos especiais.
midjji
32

Na página do manual 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
fonte
3
Boa resposta, mas o link está morto. Sugiro este em seu lugar.
Abderrahim Kitouni
30

C ++ 20 std::numbers::pi

Finalmente chegou: http://eel.is/c++draft/numbers

Espero que o uso seja como:

#include <numbers>
#include <iostream>

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

Vou tentar quando o suporte chegar ao GCC, o GCC 9.1.0 com g++-9 -std=c++2aainda não o suporta.

A proposta aceita descreve:

5.0 “Cabeçalhos” [cabeçalhos] Na tabela [guia: cpp.library.headers], um novo<math> cabeçalho precisa ser adicionado.

[...]

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

Há também um std::numbers::e claro :-) Como calcular a constante de Euler ou a alimentação de Euler em C ++?

Essas constantes usam o recurso de modelo de variável C ++ 14: C ++ 14: Modelos de variável C ++ 14: qual é o seu objetivo? Algum exemplo de uso?

Nas versões anteriores do rascunho, a constante estava em std::math::pi: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf

Ciro Santilli adicionou uma nova foto
fonte
27

O C ++ padrão não tem uma constante para o PI.

Muitos compiladores C ++ definem M_PIin cmath(ou in math.hfor C) como uma extensão não padrão. Você pode precisar fazer isso #define _USE_MATH_DEFINESantes de poder vê-lo.

RichieHindle
fonte
18

eu faria

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

ou

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

Eu não digitaria π com a precisão que você precisa . O que isso quer dizer? A precisão de que você precisa é a precisão T, mas não sabemos nada sobreT .

Você pode dizer: Do que você está falando? Tserá float, doubleou long double. Então, basta digitar a precisão de long double, ie

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

Mas você realmente sabe que no futuro não haverá um novo tipo de ponto flutuante com uma precisão ainda mais alta do que long double ? Você não

E é por isso que a primeira solução é linda. Você pode ter certeza de que o padrão sobrecarregaria as funções trigonométricas para um novo tipo.

E, por favor, não diga que a avaliação de uma função trigonométrica na inicialização é uma penalidade de desempenho.

0xbadf00d
fonte
1
Note isso arg(log(x)) == πpara todos 0 < x < 1.
0xbadf00d
Esta é uma péssima ideia. use um modelo sobrecarregado por tipo constexpr, para obter um erro de compilação para forçá-lo a defini-lo se um novo tipo aparecer. Também é geralmente terrível, porque os tipos trigonométricos não se limitam aos tipos de ponto flutuante. Portanto, aproveite o erro atan (1) ... O padrão não garante que as funções trigonométricas calculem seus valores trigonométricos reais com a precisão do tipo. Geralmente não o fazem, e isso piora com, por exemplo, fastmath e é sempre especialmente ruim para os valores especiais.
midjji
10

Eu uso o seguinte em um dos meus cabeçalhos comuns no projeto que abrange todas as 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

Em uma nota lateral, todos os compiladores abaixo definem constantes M_PI e M_PIl se você incluir <cmath>. Não há necessidade de adicionar `#define _USE_MATH_DEFINES, que é necessário apenas para o VC ++.

x86 GCC 4.4+
ARM GCC 4.5+
x86 Clang 3.0+
Shital Shah
fonte
O downvoter pode comentar o que está errado com esta resposta. Isso é bem pesquisado e testado e está sendo usado em sistemas reais. Definitivamente, eu gostaria de melhorar se algo estiver errado.
Shital Shah
1
Para sua informação, os compiladores Borland C ++ também definem M_PIsem a necessidade de_USE_MATH_DEFINES
Remy Lebeau
8

Geralmente, prefiro definir o meu: const double PI = 2*acos(0.0); porque nem todas as implementações fornecem isso para você.

A questão de saber se essa função é chamada em tempo de execução ou estática em tempo de compilação geralmente não é um problema, porque só acontece uma vez.

Sumudu Fernando
fonte
8
acos (-1) também é pi.
Roderick Taylor
3
Geralmente, são menos instruções da CPU e / ou menos latência para carregar um operando imediato do que ler um operando de um local de memória. Além disso, apenas expressões conhecidas em tempo de compilação podem ser pré-calculadas (quero dizer, double x = pi * 1.5;etc.). Se você pretende usar o PI em matemática crocante em loops apertados, é melhor garantir que o valor seja conhecido pelo compilador.
Eugene Ryabtsev 19/08/19
7

Acabei de encontrar este artigo de Danny Kalev, que tem uma ótima dica para C ++ 14 e superior.

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

Achei isso muito legal (embora eu usasse o PI de maior precisão possível), principalmente porque os modelos podem usá-lo com base no 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>
Beta Jester
fonte
4

Valores como M_PI, M_PI_2, M_PI_4, etc não são C ++ padrão, portanto, um constexpr parece uma solução melhor. Diferentes expressões const podem ser formuladas para calcular o mesmo pi e me preocupa se elas (todas) me fornecem a precisão total. O padrão C ++ não menciona explicitamente como calcular pi. Portanto, eu tendem a voltar a definir pi manualmente. Gostaria de compartilhar a solução abaixo, que suporta todos os tipos de frações de pi com total precisão.

#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
fonte
2
Muito agradável. Pode ser necessário ter um "l" ou "L" no final desse número. Recebo um aviso de restrição do meu compilador gcc no linux.
Grant Rostig 02/02/19
2

No windows (cygwin + g ++), achei necessário adicionar a flag -D_XOPEN_SOURCE=500para o pré-processador processar a definição de M_PIin math.h.

Papa Smurf
fonte
2
Esta não é uma resposta, mas um comentário à resposta de fritzone.
0xbadf00d
2
@ 0xbadf00d: É uma resposta completamente independente que fornece as etapas necessárias para começar a M_PItrabalhar em uma plataforma específica. Isso não é um comentário em uma resposta para outra plataforma, assim como uma resposta para outra plataforma é um comentário sobre essa.
Ben Voigt
2

C ++ 14 permite que você faça static constexpr auto pi = acos(-1);

Willy Goat
fonte
9
std::acosnão é um constexpr. Portanto, seu código não será compilado.
0xbadf00d
@ 0xbadf00d eu compilado com g ++
Willy Goat
12
@WillyGoat: Então g ++ é errado, porque acosnão é constexprem C ++ 14, e não é proposta para tornar-se constexprmesmo em C ++ 17
Ben Voigt
@BenVoigt existem funções matemáticas constexpr? Aparentemente não: stackoverflow.com/questions/17347935/constexpr-math-functions
wcochran 14/02
1
@wcochran: Existem diversas funções matemáticas NOVAS constexpr, veja, por exemplo ( github.com/kthohr/gcem ). Mas eles não são compatíveis com as funções C de mesmo nome, portanto não podem assumir o controle dos nomes antigos.
Ben Voigt
2

Algumas soluções elegantes. Duvido que a precisão das funções trigonométricas seja igual à precisão dos tipos. Para aqueles que preferem escrever um valor constante, isso funciona para g ++: -

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

A precisão de 256 dígitos decimais deve ser suficiente para qualquer tipo futuro longo, longo e longo. Se mais forem necessárias, visite https://www.piday.org/million/ .

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

Você consegue fazer isso:

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

Se M_PIjá estiver definido cmath, isso não fará nada além de incluir cmath. Se M_PInão estiver definido (como é o caso, por exemplo, no Visual Studio), ele será definido. Nos dois casos, você pode usar M_PIpara obter o valor de pi.

Esse valor de pi vem do qmath.h do Qt Creator.

Pato Donald
fonte
1

Você pode usar isso:

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

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

As constantes matemáticas não são definidas no padrão C / C ++. Para usá-los, você deve primeiro definir _USE_MATH_DEFINESe incluir cmathou math.h.

Fazlı KUZU
fonte