Cómo usar la constante PI en C ++

476

Quiero usar la constante PI y las funciones trigonométricas en algún programa de C ++. Obtengo las funciones trigonométricas con include <math.h>. Sin embargo, no parece haber una definición para PI en este archivo de encabezado.

¿Cómo puedo obtener PI sin definirlo manualmente?

Etan
fuente
3
@tiwo, estás preguntando ¿cuál es la diferencia entre 3.14, 3.141592y atan(1) * 4?
Nikola Malešević
21
Como nota al margen, cmath debe usarse en C ++ en lugar de math.h, que es para C.
juzzlin
44
Relacionado libremente: vea cise.ufl.edu/~manuel/obfuscate/pi.c sobre cómo calcular el valor de PI directamente desde la definición.
lorro
3
¡Ha llegado en C ++ 20! stackoverflow.com/a/57285400/895245
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

537

En algunas plataformas (especialmente las más antiguas) (vea los comentarios a continuación) es posible que necesite

#define _USE_MATH_DEFINES

y luego incluya el archivo de encabezado necesario:

#include <math.h>

y se puede acceder al valor de pi a través de:

M_PI

En mi math.h(2014) se define como:

# define M_PI           3.14159265358979323846  /* pi */

pero revisa tu math.hpara más. Un extracto de lo "viejo" math.h(en 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.
 */

Sin embargo:

  1. en plataformas más nuevas (al menos en mi Ubuntu 14.04 de 64 bits) no necesito definir el _USE_MATH_DEFINES

  2. En plataformas Linux (recientes) también hay long doublevalores proporcionados como una extensión GNU:

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */
Ferenc Deak
fuente
51
#define _USE_MATH_DEFINESseguido de #include <math.h>define M_PIen visual c ++. Gracias.
Etan el
3
Funciona con encabezados cygwin también.
Rob
24
Siempre puedes incluir en cmathlugar de math.h.
Richard J. Ross III
10
Incluso después de definir _USE_MATH_DEFINESsi GCC se queja, eso se debe a que __STRICT_ANSI__está definido (tal vez lo aprobó -pedantico -std=c++11) lo que no se M_PIpuede definir, por lo tanto, no lo defina con -D__STRICT_ANSI__. Al definirlo usted mismo, ya que es C ++, en lugar de una macro, debería hacerlo constexpr auto M_PI = 3.14159265358979323846;.
legends2k
1
A partir de 2018, la respuesta definitivamente debe actualizarse para usar <cmath> en lugar de <math.h>
jaskmar
170

Pi se puede calcular como atan(1)*4. Puede calcular el valor de esta manera y almacenarlo en caché.

Konamiman
fuente
78
Para usuarios de c ++ 11:constexpr double pi() { return std::atan(1)*4; }
matiu
41
-1: funciona solo si atan(1)*4 == 3.141592653589793238462643383279502884(más o menos). No apostaría por eso. Sea normal y use un literal en bruto para definir la constante. ¿Por qué perder precisión cuando no es necesario?
Thomas Eding
29
Se puede evitar la operación de multiplicación con atan2(0, -1);.
legends2k
44
@matiu atanno lo es constexpr.
R. Martinho Fernandes
45
Intente en su acos(-1)lugar, no es necesario atan2.
user541686
113

También puede usar boost, que define constantes matemáticas importantes con la máxima precisión para el tipo solicitado (es decir, float vs double).

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

Consulte la documentación de impulso para obtener más ejemplos.

BuschnicK
fuente
184
Impulso: ¡Impulsando la complejidad ya innecesaria de C ++ desde 1999!
Dan Moulding
47
Pegadizo y en parte cierto. Por otro lado, el impulso puede ser fenomenalmente útil a veces ...
BuschnicK
59
@DanMoulding: Uhm. ¿Es C el único otro idioma que conoces? Porque todos los otros lenguajes que conozco, excepto C, tienen una biblioteca estándar que es de magnitud mayor que C ++ '(por ejemplo, Python, Haskell, C #, PHP, Delphi, Erlang, Java, ......). Por experiencia personal, esa opinión elitista not gonna use libses una plaga y probablemente la razón número uno para un mal software escrito en C ++.
Sebastian Mach
11
@Gracchus: Sí. C ++ sin bibliotecas (o sin las nuevas bibliotecas C ++ 11) es, por mucho que me guste ese lenguaje y tanto como me gustaría codificar todo por mí mismo, no muy productivo.
Sebastian Mach
14
Creo que dijo que la complejidad no es el tamaño . Presumiblemente refiriéndose a a) los 3 espacios de nombres anidados, yb) definiendo pi como una función con plantilla en lugar de solo una constante normal.
Timmmm
83

Obténgalo de la unidad FPU en chip en su lugar:

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

double PI = get_PI();
Henrik
fuente
40
:-) probablemente no sea esa plataforma independiente, ¡sino una buena solución exótica adicional!
Etan
3
Me encanta cómo
pensaste
1
Me encanta esta respuesta Es particularmente útil cuando se dirige a plataformas x86 más antiguas, lo cual es una moda reciente en los últimos tiempos en que los compiladores de optimización no están tan involucrados como los modernos. Gracias por esto Henrik!
Matt
49

Recomendaría simplemente escribir pi con la precisión que necesita. Esto no agregaría tiempo de cálculo a su ejecución, y sería portátil sin usar encabezados o #defines. Calcular acos o atan siempre es más costoso que usar un valor precalculado.

const double PI  =3.141592653589793238463;
const float  PI_F=3.14159265358979f;
Alex
fuente
28
Este es un gran ejemplo de por qué no debemos adoptar este enfoque, las personas cometemos errores, redondeamos, copiamos y pegamos, etc. Creo que usar M_PI es el enfoque correcto.
nacho4d
10
Si uno está haciendo esto en C ++ 11, haga el consta constexpr.
legends2k
3
@ nacho4d También prefiero M_PI si está disponible, pero no todos los sistemas son compatibles con POSIX. Creo que este enfoque es mejor que el método 4 * atan (1) para los casos en que M_PI no está disponible.
m24p
2
"Calcular acos o atan siempre es más caro" no es cierto. Cualquier compilador de optimización moderno sabe todo acerca de las funciones matemáticas estándar y puede propagarse constantemente a través de ellas. Véase, por ejemplo, goo.gl/BvdJyr
Nemo el
2
@Nemo, ejemplo de Counter: godbolt.org/g/DsAern Como se ha dicho en otra parte, parece que solo GCC hace esto actualmente y es probable porque ha declarado las funciones matemáticas básicas como constexpr.
Parker Coates
47

En lugar de escribir

#define _USE_MATH_DEFINES

Recomendaría usar -D_USE_MATH_DEFINESo /D_USE_MATH_DEFINESdependiendo de su compilador.

De esta manera, tiene la seguridad de que incluso en el caso de que alguien incluya el encabezado antes de hacerlo (y sin el #define), seguirá teniendo las constantes en lugar de un oscuro error del compilador que tardará años en rastrear.

Matthieu M.
fuente
Buen consejo. Si "usted" es una unidad de compilación, por supuesto, puede asegurarse de que la macro esté definida antes de incluir nada. Pero si "usted" es un archivo de encabezado, está fuera de su control.
Steve Jessop
3
De hecho, incluso si "usted" es una unidad de compilación ... dependiendo del orden de las cabeceras es un camino más corto hacia la pesadilla de mantenimiento ...
Matthieu M.
1
Sin embargo, no tiene que depender del orden de los encabezados. No importa si los encabezados se incluyen entre sí, siempre que haga la #definición antes de #incluir algo (al menos, suponiendo que nada #lo defina). Lo mismo se aplica a NDEBUG.
Steve Jessop el
1
El problema muy común en un proyecto es que si está compilando con Visual Studio, por ejemplo, no sabe en qué orden el compilador revisará sus archivos, por lo que si lo usa <cmath>en diferentes lugares, se convierte en un gran dolor (especialmente si está incluido en otra biblioteca que está incluyendo). Hubiera sido mucho mejor si pusieran esa parte fuera de los protectores de cabecera, pero ahora no pueden hacer mucho al respecto. La directiva del compilador funciona bastante bien.
meneldal
40

Dado que la biblioteca estándar oficial no define un PI constante, tendría que definirlo usted mismo. Entonces, la respuesta a su pregunta "¿Cómo puedo obtener PI sin definirlo manualmente?" es "No lo haces, o confías en algunas extensiones específicas del compilador". Si no le preocupa la portabilidad, puede consultar el manual de su compilador para esto.

C ++ te permite escribir

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

pero no se garantiza que la inicialización de esta constante sea estática. Sin embargo, el compilador de G ++ maneja esas funciones matemáticas como intrínsecas y puede calcular esta expresión constante en tiempo de compilación.

sellibitze
fuente
66
Usualmente uso acos (-1), como dices, se evalúan en tiempo de compilación. Cuando probé M_PI, acos (-1) y atan (1) * 4, obtuve valores idénticos.
Micah
2
La forma tradicional es usar 4*atan(1.): atanes fácil de implementar y multiplicar por 4 es una operación exacta. Por supuesto, los compiladores modernos doblan (apuntan a doblar) todas las constantes con la precisión requerida, y es perfectamente razonable usarlo acos(-1)o incluso lo std::abs(std::arg(std::complex<double>(-1.,0.)))que es el inverso de la fórmula de Euler y, por lo tanto, más estéticamente agradable de lo que parece (he agregado absporque no lo hago ' No recuerde cómo se corta el plano complejo o si está definido).
tobi_s
Solo para que nadie piense accidentalmente que hablas en serio (de nuevo -_- '). Esta es una solución terrible. La implementación de atan no está definida por el estándar que significa su implementación y posiblemente depende de hw. Esto significa que los números pueden ser terribles, lo que significa que es mejor que use 3.14 en general. Además, es posiblemente lento, incluso para los casos especiales.
midjji
32

Desde la página de manual de 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
fuente
3
Buena respuesta pero el enlace está muerto. Sugiero este en su lugar.
Abderrahim Kitouni
30

C ++ 20 std::numbers::pi

Por fin, ha llegado: http://eel.is/c++draft/numbers

Espero que el uso sea así:

#include <numbers>
#include <iostream>

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

Lo intentaré cuando llegue el soporte a GCC, GCC 9.1.0 con g++-9 -std=c++2atodavía no lo admite.

La propuesta aceptada describe:

5.0. “Encabezados” [encabezados] En la tabla [pestaña: cpp.library.headers], se <math>debe agregar un nuevo encabezado.

[...]

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

También hay, std::numbers::epor supuesto :-) ¿Cómo calcular la constante de Euler o Euler con C ++?

Estas constantes utilizan la función de plantilla variable C ++ 14: Plantillas variables C ++ 14: ¿cuál es su propósito? ¿Algún ejemplo de uso?

En versiones anteriores del borrador, la constante estaba en std::math::pi: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
fuente
27

El estándar C ++ no tiene una constante para PI.

Muchos compiladores de C ++ se definen M_PIen cmath(o en math.hC) como una extensión no estándar. Puede que tenga que hacerlo #define _USE_MATH_DEFINESantes de poder verlo.

RichieHindle
fuente
18

yo lo haría

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

o

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

Yo no escribir en π a la precisión que necesita . ¿Qué se supone que significa eso? La precisión que necesita es la precisión de T, pero no sabemos nada al respecto T.

Podrías decir: ¿De qué estás hablando? Tserá float, doubleo long double. Entonces, solo escriba la precisión de long double, es decir

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

Pero, ¿sabe realmente que no habrá un nuevo tipo de coma flotante en el estándar en el futuro con una precisión aún mayor que long double? Usted no

Y es por eso que la primera solución es hermosa. Puede estar bastante seguro de que el estándar sobrecargaría las funciones trigonométricas para un nuevo tipo.

Y, por favor, no diga que la evaluación de una función trigonométrica en la inicialización es una penalización de rendimiento.

0xbadf00d
fuente
1
Tenga en cuenta que arg(log(x)) == πpara todos 0 < x < 1.
0xbadf00d
Esta es una idea terrible. use una plantilla sobrecargada por tipo constexpr, de esa forma obtendrá un error de compilación que lo obligará a definirlo si aparece un nuevo tipo. También es generalmente terrible porque los tipos de trigonometría no se limitan a los tipos de coma flotante. Así que disfrute el error atan (1) ... El estándar no garantiza que las funciones trigonométricas calculen sus valores trigonométricos reales con la precisión del tipo. Generalmente no lo hacen, y empeora con, por ejemplo, la matemática rápida y siempre es especialmente malo para los valores especiales.
midjji
10

Uso siguiente en uno de mis encabezados comunes en el proyecto que cubre todas las 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

En una nota al margen, todos los compiladores a continuación definen las constantes M_PI y M_PIl si las incluye <cmath>. No es necesario agregar `#define _USE_MATH_DEFINES, que solo es necesario para VC ++.

x86 GCC 4.4+
ARM GCC 4.5+
x86 Clang 3.0+
Shital Shah
fuente
¿Puede el votante comentar lo que está mal con esta respuesta? Esto está bien investigado y probado y está en uso en un sistema real. Definitivamente me gustaría mejorarlo si algo está mal.
Shital Shah
1
Para su información, los compiladores de Borland C ++ también definen M_PIsin necesidad_USE_MATH_DEFINES
Remy Lebeau
8

Generalmente prefiero definir el mío: const double PI = 2*acos(0.0);porque no todas las implementaciones se lo proporcionan.

La cuestión de si esta función se llama en tiempo de ejecución o está estática en tiempo de compilación generalmente no es un problema, porque solo ocurre una vez de todos modos.

Sumudu Fernando
fuente
8
acos (-1) también es pi.
Roderick Taylor
3
A menudo es menos instrucciones de CPU y / o menos latencia cargar un operando inmediato que leer un operando desde una ubicación de memoria. Además, solo las expresiones que se conocen en tiempo de compilación podrían calcularse previamente (quiero decir double x = pi * 1.5;y similares). Si alguna vez tiene la intención de utilizar PI en matemáticas crujientes en bucles ajustados, es mejor asegurarse de que el compilador conozca el valor.
Eugene Ryabtsev
7

Acabo de encontrar este artículo de Danny Kalev que tiene un gran consejo para C ++ 14 y versiones posteriores.

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

Pensé que esto era bastante bueno (aunque usaría el PI de mayor precisión que pudiera), especialmente porque las plantillas pueden usarlo según el 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
fuente
4

Los valores como M_PI, M_PI_2, M_PI_4, etc. no son C ++ estándar, por lo que un constexpr parece una mejor solución. Se pueden formular diferentes expresiones constantes que calculen el mismo pi y me preocupa si (todas) me proporcionan la precisión completa. El estándar C ++ no menciona explícitamente cómo calcular pi. Por lo tanto, tiendo a recurrir a definir pi manualmente. Me gustaría compartir la solución a continuación que admite todo tipo de fracciones de pi con total precisión.

#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
fuente
2
Muy agradable. Puede ser necesario tener una "l" o "L" al final de ese número. Recibo una advertencia de reducción de mi compilador gcc en linux.
Grant Rostig
2

En windows (cygwin + g ++), he encontrado necesario agregar el indicador -D_XOPEN_SOURCE=500para que el preprocesador procese la definición de M_PIin math.h.

papá Pitufo
fuente
2
Esta no es una respuesta, sino un comentario a la respuesta de fritzone.
0xbadf00d
2
@ 0xbadf00d: es una respuesta completamente independiente que proporciona los pasos necesarios para M_PItrabajar en una plataforma en particular. Ese no es un comentario sobre una respuesta para alguna otra plataforma, más que una respuesta para otra plataforma es un comentario sobre esta.
Ben Voigt
2

C ++ 14 te permite hacer static constexpr auto pi = acos(-1);

Cabra Willy
fuente
99
std::acosno es una constexpr. Entonces, su código no se compilará.
0xbadf00d
@ 0xbadf00d Lo compilé con g ++
Willy Goat
12
@WillyGoat: Entonces g ++ está mal, porque acosno está constexpren C ++ 14, y no se propone que sea constexprpar en C ++ 17
Ben Voigt
@BenVoigt ¿hay alguna función matemática que sea constexpr? Aparentemente no: stackoverflow.com/questions/17347935/constexpr-math-functions
wcochran
1
@wcochran: Hay muchas NUEVAS funciones matemáticas que se encuentran constexpr, por ejemplo ( github.com/kthohr/gcem ). Pero no son compatibles con las funciones C del mismo nombre, por lo que no pueden hacerse cargo de los nombres antiguos.
Ben Voigt
2

Algunas soluciones elegantes. Sin embargo, dudo que la precisión de las funciones trigonométricas sea igual a la precisión de los tipos. Para aquellos que prefieren escribir un valor constante, esto funciona para g ++: -

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

La precisión de 256 dígitos decimales debería ser suficiente para cualquier tipo doble futuro largo largo largo. Si se requieren más, visite https://www.piday.org/million/ .

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

Puedes hacerlo:

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

Si M_PIya está definido en cmath, esto no hará nada más que incluir cmath. Si M_PIno está definido (como es el caso, por ejemplo, en Visual Studio), lo definirá. En ambos casos, puedes usarM_PI para obtener el valor de pi.

Este valor de pi proviene del qmath.h de Qt Creator.

Pato Donald
fuente
1

Puedes usar eso:

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

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

Las constantes matemáticas no están definidas en el estándar C / C ++. Para usarlos, primero debe definir _USE_MATH_DEFINESy luego incluir cmatho math.h.

Fazlı KUZU
fuente