如何在c++中使用圆周率常数

我想在一些c++程序中使用PI常数和三角函数。我用include <math.h>得到三角函数。然而,在这个头文件中似乎没有PI的定义。

如何获得PI而不手动定义它?

1487798 次浏览

Pi可以计算为atan(1)*4。您可以这样计算值并缓存它。

标准c++没有圆周率的常数。

许多c++编译器在cmath中定义M_PI(或在C的math.h中定义)作为一个非标准扩展。你可能需要#define _USE_MATH_DEFINES才能看到它。

在一些(特别是旧的)平台上(参见下面的评论),您可能需要这样做

#define _USE_MATH_DEFINES

然后包含必要的头文件:

#include <math.h>

PI的值可以通过:

M_PI

在我的math.h(2014)中,它被定义为:

# define M_PI           3.14159265358979323846  /* pi */

但请查看math.h查看更多内容。摘自“旧的”math.h(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.
*/

然而:

  1. 在更新的平台上(至少在我的64位Ubuntu 14.04上),我不需要定义_USE_MATH_DEFINES

  2. 在(最近的)Linux平台上,long double值也作为GNU扩展提供:

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */
    

因为官方标准库没有定义常数PI,你必须自己定义它。因此,对于你的问题“如何获得PI而不手动定义它?”的答案是“你没有——或者你依赖于一些特定于编译器的扩展。”如果你不关心可移植性,你可以查看编译器手册。

c++允许你编写

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

但是这个常数的初始化不能保证是静态的。然而,g++编译器将这些数学函数作为内在函数处理,并能够在编译时计算这个常量表达式。

而不是写作

#define _USE_MATH_DEFINES

我建议使用-D_USE_MATH_DEFINES/D_USE_MATH_DEFINES,这取决于你的编译器。

通过这种方式,即使有人在您之前包含了头文件(并且没有使用#define),您仍然可以得到常量,而不是一个晦涩的编译器错误,您需要花费很长时间来查找。

你也可以使用boost,它为所请求的类型定义了最精确的重要数学常数(例如float vs double)。

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

查看提高文档以获得更多示例。

我通常更喜欢定义我自己的:const double PI = 2*acos(0.0);,因为不是所有的实现都为您提供它。

这个函数是在运行时被调用还是在编译时被静态输出的问题通常不是问题,因为它只会发生一次。

我建议你只输入你需要的精度。这不会为您的执行增加计算时间,并且无需使用任何头文件或#define即可移植。计算acos或atan总是比使用预先计算的值更昂贵。

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

math.h的Posix手册页:

   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

在windows (cygwin + g++)上,我发现有必要添加标记-D_XOPEN_SOURCE=500,以便预处理器处理math.hM_PI的定义。

而是从芯片上的FPU单元获取:

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


double PI = get_PI();

我会这么做

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

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

我会 输入π到你需要的精度。这到底是什么意思?你需要的精度T的精度,但我们对T一无所知。

你可能会说:你在说什么?#EYZ0将是#EYZ1, #EYZ2或long double。因此,只需输入long double的精度,即。

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

但是你真的知道未来标准中不会有比long double精度更高的新浮点类型吗?你不。

这就是为什么第一个解很漂亮。可以肯定的是,这个标准将会使三角函数过载而产生一种新的类型。

请不要说三角函数在初始化时的计算是性能损失。

c++ 14允许您执行static constexpr auto pi = acos(-1);

我在项目中使用了一个覆盖所有基础的公共头文件:

#define _USE_MATH_DEFINES
#include <cmath>


#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif


#ifndef M_PIl
#define M_PIl (3.14159265358979323846264338327950288)
#endif

另外,如果包含<cmath>,下面所有的编译器都定义了M_PI和M_PIl常量。不需要添加只有vc++才需要的#define _use_math_definitions。

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

你可以这样做:

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

如果M_PI已经在cmath中定义,那么除了包含cmath之外,它不会做任何其他事情。如果M_PI没有定义(例如在Visual Studio中就是这种情况),它将定义它。在这两种情况下,您都可以使用M_PI来获取pi的值。

这个圆周率值来自Qt Creator的qmath.h。

我刚刚看到了这篇文章 by 丹尼卡莱弗,它是c++ 14及以上版本的一个很好的提示。

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

我认为这非常酷(尽管我会在其中使用最高精度的PI),特别是因为模板可以基于类型使用它。

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>

M_PI, M_PI_2, M_PI_4等值不是标准的c++,因此constexpr似乎是更好的解决方案。不同的const表达式可以计算相同的pi,它关心我是否他们(所有)提供了完整的精度。c++标准没有明确提到如何计算圆周率。因此,我倾向于手动定义圆周率。我想分享下面的解决方案,它支持圆周率的所有分数的完全准确。

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

一些优雅的解决方案。不过,我怀疑三角函数的精度是否等于类型的精度。对于那些喜欢编写常量值的人来说,这适用于g++:-

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

256十进制数字的精度应该足以用于任何未来的长长长双精度类型。如果需要更多,请访问https://www.piday.org/million/

20 # EYZ0 c++

最后,它到达了: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;
}

其中,精确的计算结果为:

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

按:如何使用Bash命令计算pi

编译并运行:

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

输出:

float       3.14159274101257324219
double      3.14159265358979311600
long double 3.14159265358979323851
exact       3.141592653589793238462643383279502884197169399375105820974944

在Ubuntu 20.04 amd64, GCC 10.2.0上测试

已接受的建议如下:

< p > 5.0。“头”(头) 在表[tab: cppp .library.]

. headers],需要添加一个新的<math>

[…]

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

当然还有std::numbers::e:-) 如何计算欧拉常数或欧拉驱动在c++ ?

这些常量使用c++ 14变量模板特性:c++ 14变量模板:它们的目的是什么?有什么使用例子吗?

在草案的早期版本中,常量位于std::math::pi: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf下面

你可以用它:

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


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

在标准C/ c++中没有定义数学常数。要使用它们,必须首先定义_USE_MATH_DEFINES,然后包含cmathmath.h

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

在c++ 20标准库中,π对于floatdoublelong double如。被定义为std::numbers::pi_v

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

并且可以专门化为用户定义的类型。

我从大学(也许是高中)就开始背圆周率到11位,所以这一直是我的首选方法:

#ifndef PI
#define PI 3.14159265359
#endif

小数点后15位让人类到达月球表面并返回。任何超出这个范围的都是天文数字。你能在一个更小的尺度上测量这个吗?其他人则花了几个月的时间计算到数万亿位数。除了记录在案之外,这没什么用。

要知道你可以把圆周率计算到任意长度,但keep是实用的。

我不喜欢#定义,因为它们是零类型安全的简单文本替换。如果省略括号,它们也会在使用表达式时引起问题。

#define T_PI 2*PI

真的应该

#define T_PI (2*PI)

我目前对这个问题的解决方案是使用常量的硬编码值,例如my_constants.hxx

namespace Constants {
constexpr double PI = 3.141... ;
}
然而,我没有硬编码的值(因为我不喜欢这种方法),相反,我使用一个单独的Fortran程序来编写这个文件。我使用Fortran是因为它完全支持四精度(VisualStudio上的c++不支持),三角函数是c++的constexpr等价函数。 例如< / p >
real(8), parameter :: pi = 4*atan(1.0d0)

毫无疑问,其他语言也可以用来做同样的事情。