¿C ++ 11, 14, 17 o 20 introduce una constante estándar para pi?

170

Hay un problema bastante tonto con el número pi en C y C ++. Hasta donde yo sé M_PIdefinido en math.hno es requerido por ningún estándar.

Los nuevos estándares de C ++ introdujeron muchas matemáticas complicadas en la biblioteca estándar: funciones hiperbólicas std::hermitey std::cyl_bessel_idiferentes generadores de números aleatorios, y así sucesivamente.

¿Alguno de los 'nuevos' estándares trajo una constante para pi? Si no, ¿por qué? ¿Cómo funciona toda esta complicada matemática sin ella?

Soy consciente de preguntas similares sobre pi en C ++ (tienen varios años y estándares de antigüedad); Me gustaría saber el estado actual del problema.

También estoy muy interesado en por qué oh por qué C ++ todavía no tiene una constante pi pero tiene muchas matemáticas más complicadas.

UPD: Sé que puedo definir pi yo mismo como 4 * atan (1) o acos (1) o doble pi = 3.14. Por supuesto. ¿Pero por qué en 2018 todavía tengo que hacerlo? ¿Cómo funcionan las funciones matemáticas estándar sin pi?

UPD2: Según este informe de viaje para la reunión del Comité C ++ en julio de 2019 en Colonia, la propuesta P0631 (constantes matemáticas) fue aceptada en C ++ 20. ¡Parece que por fin tendremos el número pi en la biblioteca estándar!

Amomum
fuente
¿Notas la existencia de viejas preguntas como la mejor plataforma independiente pi constante? . Si le preocupa que estén desactualizados, siempre puede otorgarle una recompensa a uno de ellos pidiendo respuestas basadas en C ++ 17, etc. Entonces todas las respuestas estarán en un solo lugar. Por qué sigue siendo una buena pregunta, pero tal vez esto debería centrarse en por qué y pedir una actualización debe ser una recompensa por las preguntas existentes.
Shafik Yaghmour
Creo que vale la pena agregar nuevas respuestas ya que C ++ 20 agregó una constante pi hasta donde yo sé
Guillaume Racicot
@GuillaumeRacicot actualicé la pregunta. No estoy seguro de si debemos abordar C ++ 20 ya que aún no está oficialmente disponible.
Amomum
@GuillaumeRacicot: Es un poco tarde para agregar uno ...
Davis Herring

Respuestas:

101

Hasta C ++ 17 pi inclusive no es una constante introducida en el lenguaje, y es un dolor en el cuello.

Soy afortunado porque uso boost y definen pi con un número suficientemente grande de decimales para incluso 128 bits long double.

Si no usa Boost, codifíquelo usted mismo. Definirlo con una función trigonométrica es tentador, pero si lo hace, no puede hacerlo constexpr. La precisión de las funciones trigonométricas tampoco está garantizada por ninguna norma know I de ( cf . std::sqrt), Por lo que realmente está en un terreno peligroso de hecho depender de una función de este tipo.

Hay una manera de obtener un constexprvalor para pi mediante metaprogramación: consulte http://timmurphy.org/2013/06/27/template-metaprogramming-in-c/


Desde C ++ 20 algunas buenas noticias. No es un defininition de pi . C ++ 20 agrega algunas constantes matemáticas en <numbers>. Por ejemplo std::numbers::pies un doubletipo.

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

Betsabé
fuente
9
@Lundin: Pero eso no puede ser constexprdesafortunadamente, por eso digo "Definirlo con una función trigonométrica es un dolor"
Bathsheba
9
¿Por qué eso importa? Pi es una constante, no está sujeta a cambios ni a nada específico de la implementación. Simplemente escriba el valor (en un objeto constante si lo desea) en el número de lugares significativos para double(o algún número ridículo si le importan los dobles hipotéticos realmente largos y largos).
R .. GitHub DEJA DE AYUDAR AL HIELO
10
@ R..El problema que tengo con eso es que lo que parece ridículo ahora puede volverse perfectamente cuerdo en 20 años más o menos. (640k de Bill Gates me vienen a la mente). Confío en Boost para mantenerse al día con la evolución de la arquitectura.
Betsabé
10
@KonradRudolph: un pi de alta precisión es importante si se implementa la reducción de rango. Por ejemplo, la constante interna Pi de x86 / x87 (mantisa completa de 64 bits) conduce a un error en el peor de los casos para entradas pequeñas a la fsininstrucción de "aproximadamente 1,37 quintillones de unidades en último lugar, dejando menos de cuatro bits correctos" , y es peor aún para entradas grandes donde la reducción de rango se ajusta varias veces. Esto es algo tangencial a las constantes utilizadas long doubleen C ++, pero de todos modos es ordenado.
Peter Cordes
31

Como otros dijeron, no existe, std::pipero si desea un PIvalor preciso , puede usar:

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

Esto supone que su implementación de C ++ produce un valor de PI correctamente redondeado acos(-1.0), que es común pero no está garantizado .

No lo es constexpr, pero en la práctica, los compiladores optimizadores como gcc y clang lo evalúan en tiempo de compilación. Sin embargo, declarar que constes importante para el optimizador hacer un buen trabajo.

0x476f72616e
fuente
2
Esto es peligroso porque la acos()función tiene una pendiente infinita en x = -1. En consecuencia, este método se basa en la acos()implementación para captar básicamente el caso de un -1argumento preciso y devolver la constante correcta directamente. Mejor usar algo como lo 4*atan(1)que es matemáticamente mucho más robusto (la pendiente de buen comportamiento en x = 1y la multiplicación por 4 siempre es precisa con las matemáticas de coma flotante).
cmaster - reinstalar a monica el
1
No se nos permite usar std::acosen una expresión constante. clang informa esto como un error. Tenga en cuenta que esta es una extensión no conforme y que eventualmente debería corregirse en gcc. Favor de referirse a esta respuesta para más detalles.
badola
31

Hasta C ++ 20, no, ninguno de los estándares introduce la constante que representaría el número pi (π). Puede aproximar el número en su código:

constexpr double pi = 3.14159265358979323846;

Otros lenguajes como C # tienen la constante declarada en sus bibliotecas.

Actualización: a partir de C ++ 20, de hecho, hay una piconstante declarada dentro del <numbers>encabezado. Se accede a través: std::numbers::pi.

Ron
fuente
66
Es posible que desee agregar inlinepara C ++ 17 +.
Deduplicador
8
Ejército de reserva. Tenga un voto positivo, pero tenga en cuenta que su definición aún es vulnerable a la imprecisión con plataformas con diferentes definiciones de double. C # lo tiene fácil ya que el doubletipo es fijo. Si estuviera en el comité de estándares de C ++, propondría algo comostd::constants<double>::pi
Bathsheba
11
@Deduplicator no es constexpr implícitamente en línea también ...?
cuando
44
@R. Es justo, aunque std::numeric_limits<double>::is_iec559;en ese caso deberías hacer una afirmación estática . Lo cual, lo confieso, es lo que tengo en mi "encabezado maestro". Tenga en cuenta que formalmente debe verificar todos los tipos de punto flotante por separado. El hecho de que uno sea IEEE754 no significa que todos lo sean.
Betsabé
2
@DanielSchepler Entonces, ¿qué se supone que es ese "número"? No sabía que hay números dobles con 16 bases.
Bћовић
29

M_PIse define por "un estándar", si no es un estándar de idioma : POSIX con la extensión X / Open System Interfaces (que es muy comúnmente compatible y requerido para la marca oficial de UNIX).

(Todavía) no está seguro de lo que habrá en C ++ 20, pero como usted preguntó: probablemente tendrá tales constantes . El documento se fusionó en la última ronda de características de C ++ 20 (para el Borrador del Comité en agosto de 2019).

Específicamente, habrá ambos std::numbers::pi(de tipo double) y una plantilla variable que puede usar si desea un tipo de punto flotante diferente, por ejemplo std::numbers::pi_v<float>. La lista completa de constantes se puede ver en [numbers.syn] .

Arenque Davis
fuente
Siéntase libre de volver a redactar mi edición como mejor le parezca: solo quería agregar la ortografía real de las nuevas constantes aquí para la posteridad.
Barry
12

Obviamente no es una buena idea porque no hay un tipo obvio con el que definir pi que sea universalmente aplicable en todos los dominios.

Pi es, por supuesto, un número irracional, por lo que no puede ser representado correctamente por ningún tipo de C ++. Podría argumentar que el enfoque natural, por lo tanto, es definirlo en el tipo de punto flotante más grande disponible. Sin embargo, el tamaño del tipo de coma flotante estándar más grande long doubleno está definido por el estándar C ++, por lo que el valor de la constante variaría entre los sistemas. Peor aún, para cualquier programa en el que el tipo de trabajo no fuera el más grande, la definición de pi sería inapropiada ya que impondría un costo de rendimiento en cada uso de pi.

También es trivial para cualquier programador encontrar el valor de pi y definir su propia constante adecuada para su uso, por lo que no ofrece ninguna gran ventaja incluirlo en los encabezados matemáticos.

Jack Aidley
fuente
10
C ++ 14 nos dio plantillas variables. ¿No es para lo que son?
Amomum
21
hablado como un verdadero miembro del comité, habla sobre todas las formas en que no se puede hacer a pesar de que se presente un camino claro hacia adelante. Vea el ejemplo sorprendentemente relevante Plantillas Variables . No creo que su respuesta sea mala, pero estoy seguro de que no ayudaría a la eterna depresión de Bjarne Stroustrup por el arrepentimiento de entregar el control del futuro de C ++ a un comité muy indeciso.
cuando
44
@snb Estoy de acuerdo en que hacer piuna constante polimórfica es el camino claro hacia adelante, en un lenguaje con inferencia de tipo Hindley-Milner. En Haskell, siempre hemos tenido pi :: Floating a => a, por lo que piautomáticamente tendría el valor 3.1415927en un Floatcontexto, 3.141592653589793en un Doublecontexto y πen un contexto de cálculo simbólico. ¿Pero a la gente realmente le gustaría tener que crear una instancia explícita del parámetro de plantilla? Parece un poco incómodo, especialmente si una long doubleimplementación fija daría resultados idénticos en la mayoría de las aplicaciones.
Leftaroundabout
2
@leftaroundabout Creo que escribir auto a = pi<float>;está completamente bien, sin duda es más legible que notorio4*atan(1)
Amomum
1
Ugh, lo rechacé haciendo clic mal y ahora no puedo deshacerlo. Lo siento. Esto merece un +1 para una explicación bastante buena del razonamiento del comité de por qué no se ha agregado, incluso si personalmente creo que el razonamiento es fundamentalmente defectuoso por las razones que la gente ya ha señalado.
Financia la demanda de Mónica el
-1

Editado: para eliminar el término necesario, porque resultó controvertido. Es demasiado de un término absoluto.

C ++ es un lenguaje grande y complejo, por eso el Comité de Estándares solo incluye cosas que son muy necesarias . En la medida de lo posible, se deja a las bibliotecas estándar sin lenguaje ... como Boost.
boost :: matemáticas :: constantes

Tiger4Hire
fuente
29
Claro, std::hermitey std::cyl_bessel_iy std::coshy std::mersenne_twister_enginey std::ranlux48y std::cauchy_distributiony std::assoc_laguerrey std::betatodos eran absolutamente necesarios, ¡todos los usamos todos los días!
Amomum
2
Estoy bastante seguro de que ni siquiera el comité mismo puede firmar la idea de que todo lo que votan es "absolutamente necesario". No se trata de la necesidad, sino de agregar valor al lenguaje: casi nada en la biblioteca estándar es necesario para escribir programas, y para el caso, la mayor parte del lenguaje central también se puede eliminar (si no le importa trabajar en un Turing tarpit, eso es El valor de la inclusión de una constante para pi se puede debatir, pero la idea de que no está ahí porque simplemente no es necesaria no retiene el agua.
Jeroen Mostert
No me quejes, solo estoy citando al comité de normas. Hubo largas discusiones abiertas sobre cuánto impulso debería incluirse en el estándar C ++ 11. La razón por la que lo hizo un subconjunto tan pequeño es porque los escritores del compilador se quejaron de la cantidad de pruebas involucradas. Por lo tanto, si algo está en los estándares, está ahí porque alguien pensó que era necesario estandarizarlo. El hecho de que no sepas por qué no significa que no haya razón.
Tiger4Hire
1
@ Tiger4Hire Estoy seguro de que hay una razón para todo, simplemente no puedo entender por qué no se agregó constante para pi cuando se agregaron muchas cosas más complejas. Constant es fácil de escribir con plantillas variables y no requerirá muchas pruebas de los escritores del compilador.
Amomum
@Amomum: Sí, agregar pi parece una pequeña sobrecarga para una gran victoria. Tenga en cuenta que personalmente preferiría ver std :: network antes que std :: math :: constants.
Tiger4Hire