C ++ 11, 14, 17 o 20 introducono una costante standard per pi?


170

C'è un problema piuttosto sciocco con il numero pi in C e C ++. Per quanto ne so M_PIdefinito in math.hnon è richiesto da nessuno standard.

I nuovi standard C ++ hanno introdotto un sacco di complicate matematiche nella libreria standard: funzioni iperboliche std::hermitee std::cyl_bessel_idiversi generatori di numeri casuali e così via e così via.

Qualcuno dei "nuovi" standard ha portato una costante per pi? In caso contrario, perché? Come funziona tutta questa complicata matematica senza di essa?

Sono a conoscenza di domande simili su pi in C ++ (hanno diversi anni e standard vecchi); Mi piacerebbe conoscere lo stato attuale del problema.

Sono anche molto interessato al perché oh perché il C ++ non ha ancora una costante pi ma ha molti calcoli matematici più complicati.

UPD: So di poter definire pi me stesso come 4 * atan (1) o acos (1) o double pi = 3.14. Sicuro. Ma perché nel 2018 devo ancora farlo? Come funzionano le funzioni matematiche standard senza pi?

UPD2: Secondo questo rapporto di viaggio per la riunione della commissione C ++ nel luglio 2019 a Colonia, la proposta P0631 (costanti matematiche) è stata accettata in C ++ 20. Quindi sembra che alla fine avremo il numero pi nella libreria standard!


Noti l'esistenza di vecchie domande come la costante pi indipendente della migliore piattaforma? . Se temi che siano obsoleti, puoi sempre impostare una taglia su uno di loro chiedendo risposte basate su C ++ 17 ecc ... Quindi tutte le risposte sarebbero in un posto. Perché è ancora una buona domanda, ma forse questo dovrebbe concentrarsi sul perché e chiedere aggiornamenti dovrebbe essere una generosità su domande esistenti.
Shafik Yaghmour,

Penso che valga la pena aggiungere nuove risposte dal momento che C ++ 20 ha aggiunto una costante pi per quanto ne so
Guillaume Racicot

@GuillaumeRacicot ho aggiornato la domanda. Non sono sicuro se dovremmo affrontare C ++ 20 poiché non è ancora ufficialmente uscito.
Amomum,

@GuillaumeRacicot: è un po 'tardi per aggiungerne uno ...
Davis Herring,

Risposte:


101

Fino al C ++ 17 pi greco non è una costante introdotta nel linguaggio, ed è un dolore al collo.

Sono fortunato perché uso boost e definiscono pi con un numero sufficientemente grande di posizioni decimali anche per un 128 bit long double.

Se non usi Boost, esegui l'hardcode da solo. Definirlo con una funzione trigonometrica è allettante, ma se lo fai non puoi farlo a constexpr. L'accuratezza delle funzioni trigonometriche non è inoltre garantita da nessuno standard che conosco ( cfr . std::sqrt), Quindi in realtà vi trovate su un terreno pericoloso e in effetti fate affidamento su tale funzione.

C'è un modo per ottenere un constexprvalore per pi usando la metaprogrammazione: vedi http://timmurphy.org/2013/06/27/template-metaprogramming-in-c/


Da C ++ 20 alcune buone notizie. V'è un defininition per pi greco . C ++ 20 aggiunge alcune costanti matematiche in <numbers>. Ad esempio std::numbers::piè un doubletipo.

Riferimento: https://en.cppreference.com/w/cpp/numeric/constants


9
@Lundin: Ma constexprsfortunatamente non può essere un , motivo per cui dico "Definirlo con una funzione di attivazione è un dolore"
Bathsheba

9
Perchè importa? Pi è una costante, non soggetta a cambiamenti o qualcosa di specifico per l'implementazione. Basta scrivere il valore (in un oggetto const se vuoi) sul numero di posti significativi per double(o un numero ridicolo se ti preoccupi di ipotetici doppi lunghi davvero lunghi).
R .. GitHub smette di aiutare ICE l'

10
@ R..Il problema che ho con quello è ciò che sembra ridicolo ora può diventare perfettamente sano in circa 20 anni. (Mi viene in mente la 640k di Bill Gates). Confido che Boost sia al passo con l'evoluzione dell'architettura.
Bathsheba,


10
@KonradRudolph: un pi di alta precisione è importante se si implementa la riduzione della portata. Ad esempio, la costante Pi interna di x86 / x87 (mantissa a 64 bit) porta a un errore nel caso peggiore per piccoli input fsinnell'istruzione di "circa 1,37 quintilioni di unità nell'ultimo posto, lasciando corretti meno di quattro bit" , ed è ancora peggio per ingressi di grandi dimensioni in cui la riduzione della portata si avvolge più volte. Questo è un po 'tangenziale per le costanti usate long doublein C ++, ma comunque pulito.
Peter Cordes,

31

Come altri hanno detto no, std::pima se vuoi un PIvalore preciso puoi usare:

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

Ciò presuppone che l'implementazione C ++ produca un valore arrotondato correttamente di PI da acos(-1.0), che è comune ma non garantito .

Non lo è constexpr, ma in pratica l'ottimizzazione di compilatori come gcc e clang lo valuta in fase di compilazione. Dichiarare che constè importante che l'ottimizzatore faccia un buon lavoro.


2
Questo è pericoloso perché la acos()funzione ha una pendenza infinita a x = -1. Di conseguenza, questo metodo si basa acos()sull'implementazione per rilevare in modo esplicito il caso di un -1argomento preciso e restituire direttamente la costante corretta. Meglio usare qualcosa di simile 4*atan(1)matematicamente molto più robusto (la pendenza ben educata x = 1e la moltiplicazione per 4 è sempre precisa con la matematica in virgola mobile).
cmaster - ripristina monica il

1
Non ci è permesso usare std::acosun'espressione costante. clang segnala questo come errore. Si prega di notare che questa è un'estensione non conforme e dovrebbe eventualmente essere risolta in gcc. Si prega di fare riferimento a questa risposta per maggiori dettagli.
badola,

31

Fino a C ++ 20, no, nessuno degli standard introduce la costante che rappresenterebbe il numero pi (π). Puoi approssimare il numero nel tuo codice:

constexpr double pi = 3.14159265358979323846;

Altri linguaggi come C # hanno la costante dichiarata nelle loro librerie.

Aggiornamento: a partire da C ++ 20, c'è effettivamente una picostante dichiarata <numbers>nell'intestazione. Vi si accede tramite: std::numbers::pi.


6
Potresti voler aggiungere inlineper C ++ 17 +.
Deduplicatore,

8
Ta. Avere un voto positivo, ma nota che la tua definizione è ancora vulnerabile all'imprecisione con piattaforme con diverse definizioni di double. C # è facile poiché il doubletipo è fisso. Se fossi nel comitato per gli standard del C ++, proporrei qualcosa del generestd::constants<double>::pi
Bathsheba,

11
@Deduplicator non è constexpr implicitamente anche in linea ...?
quando l'

4
@R. Abbastanza giusto, anche se std::numeric_limits<double>::is_iec559;in questo caso dovresti affermare staticamente . Che, lo confesso, è quello che ho nella mia "intestazione principale". Si noti che formalmente è necessario verificare separatamente tutti i tipi in virgola mobile. Solo perché uno è IEEE754 non significa che lo siano tutti.
Bathsheba,

2
@DanielSchepler Allora, quale dovrebbe essere quel "numero"? Non sapevo che ci fossero numeri doppi con 16 basi.
BЈовић

29

M_PIè definito da "uno standard", se non uno standard linguistico : POSIX con l'estensione X / Open System Interfaces (che è molto comunemente supportata e richiesta per il marchio ufficiale UNIX).

Non è (ancora) certo quale sarà in C ++ 20, ma da quando hai chiesto: probabilmente avrà tali costanti . Il documento è stato unito nell'ultimo round di funzionalità di C ++ 20 (per il Committee Draft di agosto 2019).

In particolare, ci saranno sia std::numbers::pi(di tipo double) sia un modello variabile che è possibile utilizzare se si desidera un diverso tipo in virgola mobile, ad es std::numbers::pi_v<float>. L'elenco completo delle costanti può essere visualizzato in [numeri.syn] .


Sentiti libero di riformulare la mia modifica come ritieni opportuno: volevo solo aggiungere l'ortografia reale delle nuove costanti qui per i posteri.
Barry,

12

Non è ovviamente una buona idea perché non esiste un tipo ovvio con cui definire pi universalmente applicabile a tutti i domini.

Pi è, ovviamente, un numero irrazionale, quindi non può essere rappresentato correttamente da alcun tipo C ++. Si potrebbe sostenere che l'approccio naturale, quindi, è quello di definirlo nel più grande tipo a virgola mobile disponibile. Tuttavia, la dimensione del tipo di virgola mobile standard più grande long doublenon è definita dallo standard C ++, quindi il valore della costante varierebbe tra i sistemi. Peggio ancora, per qualsiasi programma in cui il tipo di lavoro non fosse di questo tipo, la definizione di pi sarebbe inappropriata poiché importerebbe un costo prestazionale su ogni uso di pi.

È anche banale per qualsiasi programmatore trovare il valore di pi e definire la propria costante adatta per l'uso, quindi non offre alcun grande vantaggio includerlo nelle intestazioni matematiche.


10
C ++ 14 ci ha fornito modelli variabili. Non è per quello che sono?
Amomum,

21
parlato come un vero membro del comitato, parla di tutti i modi in cui non può essere fatto nonostante sia presentato chiaramente il percorso da seguire. Vedi l'esempio sorprendentemente rilevante Modelli di variabili . Non credo che la tua risposta sia negativa, ma sono sicuro che non aiuterebbe l'eterna depressione di Bjarne Stroustrup per il rimpianto di aver consegnato il controllo del futuro del C ++ a un comitato molto indeciso.
whn

4
@snb Sono d'accordo sul fatto che fare piuna costante polimorfica sia il chiaro percorso da seguire - in una lingua con inferenza di tipo Hindley-Milner. In Haskell, l'abbiamo sempre avuto pi :: Floating a => a, quindi piavrebbe automaticamente il valore 3.1415927in un Floatcontesto, 3.141592653589793in un Doublecontesto e πin un contesto di calcolo simbolico. Ma alla gente piacerebbe davvero dover istanziare esplicitamente il parametro template? Sembra un po 'imbarazzante, specialmente se long doubleun'implementazione fissa darebbe risultati identici nella maggior parte delle applicazioni.
lasciato circa l'

2
@leftaroundabout Credo che la scrittura vada auto a = pi<float>;benissimo, sicuramente più leggibile che famigerata4*atan(1)
Amomum

1
Ugh, l'ho sottovalutato per errore e ora non posso annullarlo. Scusate. Questo merita un +1 per una spiegazione abbastanza buona del ragionamento della commissione sul perché non è stato aggiunto, anche se personalmente ritengo che il ragionamento sia fondamentalmente imperfetto per i motivi che le persone hanno già sottolineato.
Finanzia la causa di Monica il

-1

Modificato - Per rimuovere il termine necessario, perché si è rivelato controverso. È troppo di un termine assoluto.

Il C ++ è un linguaggio ampio e complesso, per questo motivo il Comitato per le norme include solo cose che sono fortemente richieste . Il più possibile è lasciato alle librerie standard non linguistiche ... come Boost.
boost :: :: costanti matematiche


29
Certo, std::hermitee e std::cyl_bessel_ie std::coshe std::mersenne_twister_enginee std::ranlux48e std::cauchy_distributione std::assoc_laguerree std::betatutto e assolutamente necessari, li usiamo tutti i giorni!
Amomum,

2
Sono abbastanza sicuro che non potresti convincere nemmeno la commissione stessa a firmare l'idea che tutto ciò in cui votano è "assolutamente necessario". Non si tratta di necessità, ma di aggiungere valore alla lingua - quasi nulla nella libreria standard è necessario per scrivere programmi, e per questo motivo anche la maggior parte del linguaggio di base può essere buttato via (se non ti dispiace lavorare in un Tarpit di Turing, cioè). Il valore dell'inclusione di una costante per pi può essere discusso, ma l'idea che non sia presente perché non è necessario non contiene acqua.
Jeroen Mostert,

Non lamentarti con me, sto solo citando il comitato per gli standard. Ci sono state lunghe discussioni aperte su quanto di boost dovrebbe essere incluso nello standard C ++ 11. Il motivo per cui un sottoinsieme così piccolo lo ha fatto è perché gli autori del compilatore si sono lamentati di quanti test fossero coinvolti. Pertanto, se qualcosa è negli standard, è lì perché qualcuno ha ritenuto necessario standardizzarlo. Solo perché non sai perché, non significa che non ci fosse motivo.
Tiger4Hire

1
@ Tiger4Hire Sono sicuro che c'è una ragione per tutto, non riesco proprio a capire perché la costante per pi non sia stata aggiunta quando c'erano molte cose più complesse. Costante è facile da scrivere con modelli variabili e non richiede molti test da parte degli autori del compilatore.
Amomum,

@Amomum: Sì, l'aggiunta di pi sembra un piccolo sovraccarico per una grande vittoria. Mente, preferirei vedere std :: network prima di std :: math :: constants.
Tiger4Hire,
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.