Hoe de PI-constante in C++ te gebruiken

Ik wil de PI-constante en trigonometrische functies gebruiken in een C++-programma. Ik krijg de trigonometrische functies met include <math.h>. Er lijkt echter geen definitie voor PI te zijn in dit headerbestand.

Hoe kan ik PI krijgen zonder deze handmatig te definiëren?

Antwoord 1, autoriteit 100%

Op sommige (vooral oudere) platforms (zie de opmerkingen hieronder) moet u wellicht

#define _USE_MATH_DEFINES

en voeg dan het benodigde headerbestand toe:

#include <math.h>

en de waarde van pi is toegankelijk via:

M_PI

In mijn math.h (2014) wordt het gedefinieerd als:

# define M_PI           3.14159265358979323846  /* pi */

maar controleer uw math.h voor meer informatie. Een uittreksel uit de “oude” math.h (in 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.
 */

Echter:

  1. op nieuwere platforms (tenminste op mijn 64 bit Ubuntu 14.04) hoef ik de _USE_MATH_DEFINES

  2. niet te definiëren

  3. Op (recente) Linux-platforms zijn er ook long double-waarden als GNU-extensie:

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */
    

Antwoord 2, autoriteit 30%

Pi kan worden berekend als atan(1)*4. Je zou de waarde op deze manier kunnen berekenen en in de cache kunnen opslaan.

Antwoord 3, autoriteit 20%

Je zou ook boost kunnen gebruiken, dat belangrijke wiskundige constanten definieert met maximale nauwkeurigheid voor het gevraagde type (d.w.z. zwevend versus dubbel).

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

Bekijk de verhoog de documentatie voor meer voorbeelden.

Antwoord 4, autoriteit 17%

Haal het in plaats daarvan van de FPU-eenheid op de chip:

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

Antwoord 5, autoriteit 14%

C++20 std::numbers::pi

Eindelijk is het er: http://eel.is/c++draft/numbers

main.cpp

#include <numbers> // std::numbers
#include <iomanip>
#include <iostream>
int main() {
    std::cout << std::fixed << std::setprecision(20);
    std::cout << "float       " << std::numbers::pi_v<float> << std::endl;
    std::cout << "double      " << std::numbers::pi << std::endl;
    std::cout << "long double " << std::numbers::pi_v<long double> << std::endl;
    std::cout << "exact       " << "3.141592653589793238462643383279502884197169399375105820974944" << std::endl;
}

waar het exacte resultaat is berekend met:

echo "scale=60; 4*a(1)" | BC_LINE_LENGTH=0 bc -l

volgens: Hoe kan ik pi berekenen met Bash-opdracht

Compileren en uitvoeren:

g++-10 -ggdb3 -O0 -std=c++20 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

Uitvoer:

float       3.14159274101257324219
double      3.14159265358979311600
long double 3.14159265358979323851
exact       3.141592653589793238462643383279502884197169399375105820974944

Getest op Ubuntu 20.04 amd64, GCC 10.2.0

Het geaccepteerde voorstel beschrijft:

5.0. Kopteksten [kopteksten]
In de tabel [tab:cpp.library.headers] moet een nieuwe <math> header worden toegevoegd.

[…]

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

Er is natuurlijk ook een std::numbers::e 🙂 Hoe bereken ik Euler-constante of Euler powered in C++?

Deze constanten gebruiken de C++14 variabele sjabloonfunctie: C++14 Variabele Sjablonen: wat is hun doel? Een gebruiksvoorbeeld?

In eerdere versies van het concept stond de constante onder std::math::pi: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf

Antwoord 6, autoriteit 9%

Ik zou aanraden om pi in te typen met de precisie die je nodig hebt. Dit zou geen rekentijd toevoegen aan uw uitvoering, en het zou draagbaar zijn zonder headers of #defines te gebruiken. Het berekenen van acos of atan is altijd duurder dan het gebruiken van een vooraf berekende waarde.

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

Antwoord 7, autoriteit 8%

In plaats van te schrijven

#define _USE_MATH_DEFINES

Ik raad aan om -D_USE_MATH_DEFINES of /D_USE_MATH_DEFINES te gebruiken, afhankelijk van je compiler.

Op deze manier bent u er zeker van dat zelfs als iemand de header opneemt voordat u dat doet (en zonder de #define), u nog steeds de constanten zult hebben in plaats van een obscure compilerfout die u eeuwen zal kosten om op te sporen.

Antwoord 8, autoriteit 7%

Aangezien de officiële standaardbibliotheek geen constante PI definieert, zou u deze zelf moeten definiëren. Dus het antwoord op uw vraag “Hoe kan ik PI krijgen zonder het handmatig te definiëren?” is “Dat doet u niet — of u vertrouwt op enkele compiler-specifieke extensies.”. Als u zich geen zorgen maakt over draagbaarheid, kunt u hiervoor de handleiding van uw compiler raadplegen.

C++ stelt je in staat om te schrijven

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

maar de initialisatie van deze constante is niet gegarandeerd statisch. De G++-compiler behandelt deze wiskundige functies echter als intrinsieke elementen en kan deze constante expressie tijdens het compileren berekenen.

Antwoord 9, autoriteit 6%

Van de Posix man-pagina van 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

Antwoord 10, autoriteit 5%

Standaard C++ heeft geen constante voor PI.

Veel C++-compilers definiëren M_PI in cmath (of in math.h voor C) als een niet-standaard extensie. Mogelijk moet je #define _USE_MATH_DEFINES voordat je het kunt zien.

Antwoord 11, autoriteit 3%

Ik zou doen

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

of

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

Ik zou niet in typen? tot de precisie die u nodig heeft. Wat moet dat eigenlijk betekenen? De precisie die je nodig hebt is de precisie van T, maar we weten niets over T.

Je zou kunnen zeggen: Waar heb je het over? T is float, double of long double. Typ dus gewoon de precisie van long double, d.w.z.

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

Maar weet je echt dat er in de toekomst geen nieuw floating point type in de standaard zal komen met een nog hogere precisie dan long double? Jij niet.

En daarom is de eerste oplossing mooi. Je kunt er vrij zeker van zijn dat de standaard de trigonometrische functies voor een nieuw type zou overbelasten.

En zeg alsjeblieft niet dat de evaluatie van een trigonometrische functie bij initialisatie een prestatieverlies is.

Antwoord 12, autoriteit 2%

Ik gebruik het volgende in een van mijn gemeenschappelijke headers in het project dat alle bases omvat:

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

Terzijde: alle onderstaande compilers definiëren M_PI- en M_PIl-constanten als je <cmath> opneemt. Het is niet nodig om `#define _USE_MATH_DEFINES toe te voegen, wat alleen vereist is voor VC++.

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

Antwoord 13

Over het algemeen geef ik er de voorkeur aan om mijn eigen te definiëren: const double PI = 2*acos(0.0); omdat niet alle implementaties dit voor je bieden.

De vraag of deze functie tijdens runtime wordt aangeroepen of tijdens het compileren statisch wordt uitgeschakeld, is meestal geen probleem, omdat het toch maar één keer gebeurt.

Antwoord 14

Ik kwam zojuist dit artikel van Danny Kalev met een goede tip voor C++14 en hoger.

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

Ik vond dit best cool (hoewel ik de hoogste precisie-PI zou gebruiken die ik kon), vooral omdat sjablonen het kunnen gebruiken op basis van 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>

Antwoord 15

Enkele elegante oplossingen. Ik betwijfel echter of de precisie van de trigonometrische functies gelijk is aan de precisie van de typen. Voor degenen die liever een constante waarde schrijven, werkt dit voor g++ :-

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

Een nauwkeurigheid van 256 decimale cijfers zou voldoende moeten zijn voor elk toekomstig lang, lang en lang dubbel type. Als er meer nodig zijn, ga dan naar https://www.piday.org/million/.

Antwoord 16

Waarden zoals M_PI, M_PI_2, M_PI_4, etc zijn geen standaard C++, dus een constexpr lijkt een betere oplossing. Er kunnen verschillende const-uitdrukkingen worden geformuleerd die dezelfde pi berekenen en het gaat mij om of ze mij (allemaal) de volledige nauwkeurigheid bieden. De C++-standaard vermeldt niet expliciet hoe pi moet worden berekend. Daarom ben ik geneigd terug te vallen op het handmatig definiëren van pi. Ik wil graag de onderstaande oplossing delen die alle soorten breuken van pi met volledige nauwkeurigheid ondersteunt.

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

Antwoord 17

In Windows (cygwin + g++), vond ik het nodig om de vlag -D_XOPEN_SOURCE=500 toe te voegen zodat de preprocessor de definitie van M_PI in math.h.

Antwoord 18

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

Antwoord 19

C++14 laat je static constexpr auto pi = acos(-1);

Antwoord 20

U kunt dit doen:

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

Als M_PI al is gedefinieerd in cmath, zal dit niets anders doen dan cmath opnemen. Als M_PI niet is gedefinieerd (wat bijvoorbeeld het geval is in Visual Studio), wordt dit gedefinieerd. In beide gevallen kun je M_PI gebruiken om de waarde van pi te krijgen.

Deze waarde van pi komt van Qt Creator’s qmath.h.

Antwoord 21

Je kunt dat gebruiken:

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

Wiskundige constanten zijn niet gedefinieerd in Standaard C/C++. Om ze te gebruiken, moet u eerst _USE_MATH_DEFINES definiëren en vervolgens cmath of math.h opnemen.

Antwoord 22

In de C++20 standaardbibliotheek, ? wordt gedefinieerd als std::numbers::pi_v voor float, double en long double, bijv.

#include <numbers>
auto n = std::numbers::pi_v<float>;

en kan gespecialiseerd zijn voor door de gebruiker gedefinieerde typen.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

5 + 2 =

Other episodes