C ++ 11, 14, 17 ou 20 introduz uma constante padrão para pi?

170

Há um problema bastante tolo com o número pi em C e C ++. Tanto quanto sei M_PIdefinido em math.hnão é exigido por nenhum padrão.

Novos padrões de C ++ introduziram muitas matemáticas complicadas na biblioteca padrão - funções hiperbólicas std::hermitee std::cyl_bessel_i, diferentes geradores de números aleatórios e assim por diante.

Algum dos 'novos' padrões trouxe uma constante para pi? Se não - por quê? Como toda essa matemática complicada funciona sem ela?

Estou ciente de perguntas semelhantes sobre pi em C ++ (elas têm vários anos e padrões); Eu gostaria de saber o estado atual do problema.

Também estou muito interessado em saber por que, oh, por que o C ++ ainda não tem uma constante pi, mas tem muito mais matemática complicada.

UPD: Eu sei que posso definir pi-me como 4 * atan (1) ou acos (1) ou pi duplo = 3,14. Certo. Mas por que em 2018 eu ainda tenho que fazer isso? Como as funções matemáticas padrão funcionam sem pi?

UPD2: De acordo com este relatório de viagem para a reunião do Comitê C ++ em julho de 2019 em Colônia, a proposta P0631 (constantes matemáticas) foi aceita no C ++ 20. Parece que finalmente teremos o número pi na biblioteca padrão!

Amomum
fonte
Você nota a existência de perguntas antigas, como a melhor plataforma independente pi constante? . Se você se preocupa que eles estejam desatualizados, sempre é possível definir uma recompensa em um deles pedindo respostas com base no C ++ 17 etc ... Então todas as respostas estariam em um só lugar. Por que ainda é uma boa pergunta, mas talvez isso deva se concentrar no porquê e solicitar atualizações deve ser uma recompensa pelas perguntas existentes.
Shafik Yaghmour 13/04/19
Eu acho que pode pena acrescentar novas respostas desde C ++ 20 adicionada uma constante pi, tanto quanto eu sei
Guillaume Racicot
@GuillaumeRacicot i atualizou a pergunta. Não tenho certeza se devemos abordar o C ++ 20, pois ainda não está oficialmente lançado.
Amomum
@GuillaumeRacicot: É um pouco tarde para adicionar uma…
Davis Herring

Respostas:

101

Até e incluindo C ++ 17 pi não é uma constante introduzida na linguagem e é uma dor no pescoço.

Tenho a sorte de usar o boost e eles definem pi com um número suficientemente grande de casas decimais por até 128 bits long double.

Se você não usar o Boost, codifique-o você mesmo. Defini-lo com uma função trigonométrica é tentador, mas se você fizer isso, não poderá fazê-lo constexpr. A precisão das funções trigonométricas também não é garantido por qualquer I Know padrão de ( cf . std::sqrt), Então realmente você está em terreno perigoso, de facto confiar em tal função.

Existe uma maneira de obter um constexprvalor para pi usando a metaprogramação: consulte http://timmurphy.org/2013/06/27/template-metaprogramming-in-c/


Do C ++ 20, algumas boas notícias. Não é um defininition para pi . O C ++ 20 adiciona algumas constantes matemáticas em <numbers>. Por exemplo, std::numbers::pié um doubletipo.

Referência: https://en.cppreference.com/w/cpp/numeric/constants

Bathsheba
fonte
9
@Lundin: Mas isso não pode ser um constexprinfelizmente, é por isso que eu digo "Definindo-lo com uma função trig é uma dor"
Bate-Seba
9
Por que isso Importa? Pi é uma constante, não está sujeita a alterações ou a qualquer implementação específica. Escreva o valor (em um objeto const, se quiser) para o número de lugares significativos double(ou algum número ridículo, se você se importa com duplas hipotéticas realmente longas).
R .. GitHub Pare de ajudar o gelo
10
@ R..O problema que tenho com isso é o que parece ridículo agora pode se tornar perfeitamente saudável daqui a 20 anos. (640k de Bill Gates vem à mente). Confio no Boost para acompanhar a evolução da arquitetura.
Bate-Seba
10
@KonradRudolph: Um pi de alta precisão é importante se estiver implementando uma redução de alcance. Por exemplo, a constante Pi interna do x86 / x87 (mantissa total de 64 bits) leva a um erro de pior caso para pequenas entradas à fsininstrução de "cerca de 1,37 quintilhão de unidades em último lugar, deixando menos de quatro bits corretos" , e é ainda pior para grandes entradas, nas quais a redução de alcance ocorre várias vezes. Isso é um tanto tangencial às constantes usadas long doubleno C ++, mas, mesmo assim, é puro.
Peter Cordes
31

Como outros disseram, não há, std::pimas se você quiser um PIvalor preciso, poderá usar:

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

Isso pressupõe que sua implementação C ++ produz um valor arredondado corretamente de PI de acos(-1.0), o que é comum, mas não garantido .

Não é constexpr, mas na prática otimizar compiladores como gcc e clang avaliá-lo em tempo de compilação. Porém, consté importante que o otimizador faça um bom trabalho.

0x476f72616e
fonte
2
Isso é perigoso porque a acos()função tem uma inclinação infinita em x = -1. Conseqüentemente, esse método depende da acos()implementação para capturar explicitamente basicamente o caso de um -1argumento preciso e retornar a constante correta diretamente. Melhor usar algo como 4*atan(1)matematicamente muito mais robusto (inclinação bem comportada x = 1e multiplicação por 4 é sempre preciso com matemática de ponto flutuante).
cmaster - restabelece monica 5/08/19
1
Não temos permissão para usar std::acosem uma expressão constante. clang relata isso como erro. Observe que esta é uma extensão não conforme e, eventualmente, deve ser corrigida no gcc. Consulte esta resposta para obter mais detalhes.
badola 12/09/19
31

Até C ++ 20, não, nenhum dos padrões introduz a constante que representaria o número pi (π). Você pode aproximar o número no seu código:

constexpr double pi = 3.14159265358979323846;

Outros idiomas, como C #, têm a constante declarada em suas bibliotecas.

Atualização: A partir do C ++ 20, de fato há uma piconstante declarada dentro do <numbers>cabeçalho. Ele é acessado via: std::numbers::pi.

Ron
fonte
6
Convém adicionar inlinepara o C ++ 17 +.
Deduplicator
8
Ta. Tenha um voto positivo, mas observe que sua definição ainda está vulnerável à imprecisão com plataformas com diferentes definições de double. C # é mais fácil, pois o doubletipo é fixo. Se eu fosse no comitê de padrões C ++ Eu proponho algo comostd::constants<double>::pi
Bate-Seba
11
@Duplicator não é constexpr implicitamente embutido também ...?
whn
4
@R. É justo, embora você deva afirmar estática std::numeric_limits<double>::is_iec559;nesse caso. Confesso que é o que tenho no meu "cabeçalho mestre". Observe que formalmente você precisa verificar todos os tipos de ponto flutuante separadamente. Só porque um é IEEE754 não significa que todos eles são.
Bate-Seba
2
@DanielSchepler Então, qual é esse "número"? Eu não sabia que existem números duplos com 16 bases.
BЈовић
29

M_PIé definido por "um padrão", se não um padrão de idioma : POSIX com a extensão X / Open System Interfaces (que é comumente suportada e necessária para a marca oficial do UNIX).

Ainda não está certo o que estará no C ++ 20, mas desde que você perguntou: provavelmente terá essas constantes . O documento foi mesclado na última rodada de recursos do C ++ 20 (para o esboço do comitê em agosto de 2019).

Especificamente, haverá std::numbers::pi(do tipo double) e um modelo de variável que você pode usar se desejar um tipo de ponto flutuante diferente, por exemplo std::numbers::pi_v<float>. A lista completa de constantes pode ser vista em [numbers.syn] .

Davis Herring
fonte
Sinta-se à vontade para reformular minha edição como achar melhor - eu só queria adicionar a ortografia real das novas constantes aqui para posteridade.
Barry
12

Obviamente, não é uma boa ideia, porque não existe um tipo óbvio com o qual defina pi que seja universalmente aplicável entre domínios.

Pi é, obviamente, um número irracional, portanto não pode ser representado corretamente por nenhum tipo de C ++. Você pode argumentar que a abordagem natural, portanto, é defini-la no maior tipo de ponto flutuante disponível. No entanto, o tamanho do maior tipo de ponto flutuante padrão long doublenão é definido pelo padrão C ++, portanto o valor da constante varia entre os sistemas. Pior ainda, para qualquer programa em que o tipo de trabalho não fosse o maior, a definição de pi seria inadequada, pois imporia um custo de desempenho a cada uso de pi.

Também é trivial para qualquer programador encontrar o valor de pi e definir sua própria constante adequada para uso, para que não ofereça nenhuma grande vantagem de incluí-lo nos cabeçalhos de matemática.

Jack Aidley
fonte
10
O C ++ 14 nos deu modelos variáveis. Não é para isso que eles servem?
Amomum
21
falado como um verdadeiro membro do comitê, fale sobre todas as maneiras pelas quais isso não pode ser feito, apesar de apresentar um caminho claro a seguir. Veja os exemplos de variáveis ​​surpreendentemente relevantes . Não acho que sua resposta seja ruim, mas tenho certeza de que não ajudaria a eterna depressão de Bjarne Stroustrup ao arrependimento de entregar o controle do futuro do C ++ a um comitê muito indeciso.
whn
4
@snb Concordo que fazer piuma constante polimórfica é o caminho claro a seguir - em um idioma com inferência do tipo Hindley-Milner. Em Haskell, sempre tivemos pi :: Floating a => a, para que pitivesse automaticamente o valor3.1415927 em um Floatcontexto, 3.141592653589793em um Doublecontexto e πem um contexto de computação simbólica. Mas as pessoas realmente gostariam de instanciar explicitamente o parâmetro do modelo? Parece um pouco estranho, especialmente se uma long doubleimplementação fixa fornecer resultados idênticos na maioria dos aplicativos.
usar o seguinte comando
2
@leftaroundabout Eu acredito que a escrita auto a = pi<float>;é completamente bem, certamente mais legível, em seguida, notório4*atan(1)
Amomum
1
Ugh, eu diminuí o voto com um clique errado e agora não posso desfazê-lo. Desculpe. Isso merece um +1 para uma boa explicação do raciocínio do comitê, por que ele não foi adicionado, mesmo que eu pessoalmente ache que o raciocínio é fundamentalmente defeituoso pelas razões pelas quais as pessoas já apontaram.
Fund Monica's Lawsuit
-1

Editado - Para remover o termo necessário, porque se mostrou controverso. É um termo muito absoluto.

C ++ é uma linguagem grande e complexa; por esse motivo, o Comitê de Padrões inclui apenas coisas que são altamente necessárias . O máximo possível é deixado para bibliotecas padrão que não sejam de idioma ... como o Boost.
boost :: math :: constantes

Tiger4Hire
fonte
29
Claro, std::hermiteestd::cyl_bessel_i e std::coshe std::mersenne_twister_enginee std::ranlux48e std::cauchy_distributione std::assoc_laguerree std::betatodos eram absolutamente necessários, todos nós os usamos todos os dias!
Amomum
2
Tenho certeza de que você não conseguiu nem mesmo o próprio comitê aprovar a idéia de que tudo em que votam é "absolutamente necessário". Não se trata de necessidade, mas de agregar valor ao idioma - quase nada na biblioteca padrão é necessário para escrever programas e, nesse caso, a maior parte do idioma principal também pode ser descartada (se você não se importa de trabalhar em um Tarpit de Turing, isto é). O valor da inclusão de uma constante para pi pode ser debatido, mas a idéia de que ela não está lá porque não é necessária não retém água.
Jeroen Mostert
Não reclame comigo, estou apenas citando o comitê de padrões. Houve longas discussões abertas sobre quanto do impulso deveria ser incluído no padrão C ++ 11. A razão pela qual um subconjunto tão pequeno foi criado é porque os escritores do compilador reclamaram da quantidade de testes envolvidos. Portanto, se algo está dentro dos padrões, está lá porque alguém achou necessário padronizá-lo. Só porque você não sabe o porquê, não significa que não havia razão.
Tiger4Hire
1
@ Tiger4Hire Tenho certeza de que há motivos para tudo, simplesmente não consigo entender por que a constante para pi não foi adicionada quando muitas coisas mais complexas eram. É fácil escrever Constant com modelos de variáveis ​​e não exigirá muitos testes dos escritores do compilador.
Amomum
@ Amomum: Sim, adicionar pi parece uma pequena sobrecarga para uma grande vitória. Pessoalmente, eu preferiria ver std :: network antes de std :: math :: constantes.
Tiger4Hire