如何在C ++中使用PI常量


476

我想在某些C ++程序中使用PI常数和三角函数。我得到的三角函数include <math.h>。但是,此头文件中似乎没有PI的定义。

如何在不手动定义的情况下获取PI?


3
@tiwo,你问什么之间的区别3.143.141592以及atan(1) * 4
NikolaMalešević2012年

21
作为边注,CMATH应该用C ++使用代替math.h中,这是C.
juzzlin

4
松散相关:有关如何直接从定义中计算PI值的信息,请参见cise.ufl.edu/~manuel/obfuscate/pi.c
lorro

Answers:


537

在某些(尤其是较旧的)平台上(请参见下面的评论),您可能需要

#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 */

51
#define _USE_MATH_DEFINES然后在Visual C ++中进行#include <math.h>定义M_PI。谢谢。
Etan

3
也可以与cygwin标头一起使用。
罗布

24
您始终可以使用cmath代替math.h
理查德·罗斯三世

10
即使在定义了_USE_MATH_DEFINESGCC是否抱怨这是因为__STRICT_ANSI__定义了(也许您通过-pedantic-std=c++11)而不允许M_PI定义,因此用来取消定义-D__STRICT_ANSI__。自己定义它时,因为它是C ++,而不是宏,所以应该constexpr auto M_PI = 3.14159265358979323846;
legends2k 2014年

1
截至2018年,答案应明确更新为使用<cmath>而不是<math.h>
jaskmar

170

Pi可以计算为atan(1)*4。您可以通过这种方式计算值并将其缓存。


78
对于c ++ 11用户:constexpr double pi() { return std::atan(1)*4; }
matiu 2012年

41
-1:仅在atan(1)*4 == 3.141592653589793238462643383279502884(大致而言)时有效。我不会打赌。正常,并使用原始文字定义常量。为什么在不需要时失去精度?
Thomas Eding 2012年

29
可以避免用进行乘法运算atan2(0, -1);
legends2k

44
@matiu atan不是constexpr
R. Martinho Fernandes 2013年

45
试一试acos(-1),不需要atan2
user541686

113

您也可以使用boost,它可以为请求的类型(即float与double)定义最大精度的重要数学常数。

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

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


184
Boost:自1999年以来提高了本已不必要的C ++复杂性!
Dan Moulding 2010年

47
吸引人且部分正确。另一方面,提升有时会非常有用……
BuschnicK,2010年

59
@DanMoulding:嗯。C是您知道的唯一其他语言吗?因为除C外,我知道的所有其他语言都有一个标准库,其大小比C ++大(例如Python,Haskell,C#,PHP,Delphi,Erlang,Java等)。从个人经验来看,精英主义not gonna use libs者是一种害虫,可能是使用C ++编写不良软件的第一原因。
塞巴斯蒂安·马赫

11
@Gracchus:是的。就我所喜欢的语言而言,没有库(或没有新的C ++ 11库)的C ++和我想自己编写的所有代码一样,效率不是很高。
塞巴斯蒂安·马赫

14
我相信他说的是复杂性而不是规模。大概是指a)3个嵌套的命名空间,以及b)将pi定义为模板函数,而不仅仅是普通常量。
Timmmm 2014年

83

而是从FPU芯片上获取它:

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

double PI = get_PI();

40
:-)可能不是那个独立于平台的,而是一个不错的其他奇特解决方案!
Etan 2015年

3
我爱你虽然开箱即用;)
VivienLeger

1
我喜欢这个答案。当针对较旧的x86平台(这是最近流行的一种)时,该功能特别有用,因为在该平台上,优化编译器并没有像现代编译器那样费劲。感谢这个亨里克!
马特

49

我建议您只输入pi即可达到所需的精度。这不会为您的执行增加任何计算时间,并且无需使用任何标题或#defines就可以移植。计算acos或atan总是比使用预先计算的值昂贵。

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

28
这是一个很好的例子,为什么我们不应该采用这种方法,我们会犯错误,四舍五入,复制和粘贴等。我认为使用M_PI是正确的方法。
nacho4d 2014年

10
如果在C ++ 11中执行此操作,则将consta设置为constexpr
legends2k 2014年

3
@ nacho4d我也更喜欢M_PI(如果有),但是并非所有系统都符合POSIX。对于无法使用M_PI的情况,我认为这种方法比4 * atan(1)方法更好。
m24p 2014年

2
“计算acos或atan总是更昂贵”是不正确的。任何现代的优化编译器都了解标准数学函数,并且可以通过它们进行常数传播。参见例如goo.gl/BvdJyr
Nemo,

2
@Nemo,反示例:godbolt.org/g/DsAern正如其他地方所说,目前似乎只有GCC这样做,这很可能是因为它已将基本数学函数声明为constexpr
帕克·寇

47

而不是写作

#define _USE_MATH_DEFINES

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

这样可以确保即使在有人在执行操作之前包括头的情况下(并且没有#define),您仍将拥有常量,而不是费时费力才能找到的晦涩的编译器错误。


好提示。如果“您”是一个编译单元,那么您当然可以确保在包含任何内容之前定义了宏。但是,如果“您”是头文件,则它不受您的控制。
史蒂夫·杰索普

3
实际上,即使“您”是一个编译单元……根据标题的顺序,也是维护噩梦的最短途径……
Matthieu M.

1
但是,您不必依赖标题的顺序。标头是否包含彼此无关紧要,只要您在包含所有内容之前先进行#define(至少,假设没有任何内容#undefdef它)。NDEBUG也是如此。
史蒂夫·杰索普

1
在项目中,一个非常普遍的问题是,例如,如果您使用Visual Studio进行编译,则您不知道编译器将以哪种顺序浏览文件,因此,如果<cmath>在不同的地方使用它,将是一个很大的痛苦(尤其是(如果它包含在另一个库中,则包括在内)。如果他们把那部分放到头球后卫的本来会好得多,但是现在不能做太多事情了。实际上,编译器指令运行良好。
2015年

40

由于官方标准库没有定义常量PI,因此您必须自己定义它。因此,您的问题“如何在不手动定义PI的情况下获得PI”的答案是?是“您不-或您依赖于某些编译器特定的扩展。”。如果您不担心可移植性,可以查看编译器手册。

C ++允许您编写

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

但是此常数的初始化不能保证是静态的。但是,G ++编译器将这些数学函数作为内在函数进行处理,并能够在编译时计算该常数表达式。


6
正如您所说,我通常使用acos(-1),它们是在编译时评估的。当我测试M_PI,acos(-1)和atan(1)* 4时,我得到了相同的值。
米卡2014年

2
传统方法是使用4*atan(1.)atan易于实现,乘以4是精确的操作。当然,现代编译器会以所需的精度来折叠(目标是折叠)所有常量,并且使用acos(-1)甚至std::abs(std::arg(std::complex<double>(-1.,0.)))是欧拉公式的倒数都是完全合理的,因此比看上去更美观(我添加了abs因为我没有记得复杂的平面是如何切割的,或者根本没有定义)。
tobi_s

只是没有人意外地认为你是认真的(再次-_-')。这是一个糟糕的解决方案。atan实施未由标准定义,这意味着其实施且可能与硬件相关。这意味着数字可能很糟糕,这意味着通常使用3.14可能会更好。而且,即使在特殊情况下,它也可能很慢。
Midjji

32

math.hPosix手册页中

   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

3
好的答案,但链接已死。我建议改用这个
Abderrahim Kitouni

30

C ++ 20 std::numbers::pi

最后,它到达了:http : //eel.is/c++draft/numbers

我希望用法是这样的:

#include <numbers>
#include <iostream>

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

当GCC支持到达时,我会尝试一下,GCC 9.1.0 g++-9 -std=c++2a仍不支持它。

接受的提案描述:

5.0。“标题” [headers]在表[tab:cpp.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::pihttp : //www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0631r7.pdf


27

标准C ++没有用于PI的常量。

许多C ++编译器将M_PIin cmath(或in 定义math.h为C)定义为非标准扩展。您可能需要#define _USE_MATH_DEFINES先查看一下。


18

我会做

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

要么

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

我会不会 在π输入你所需要的精度。那到底是什么意思?在你需要的精度是精度T,但我们并不知道T

您可能会说:您在什么?Tfloatdoublelong double。因此,只需输入的精度long double,即

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

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

这就是第一个解决方案很漂亮的原因。您可以肯定,该标准将使一种新类型的三角函数超载。

而且,请不要说初始化时对三角函数的求值是性能的损失。


1
注意arg(log(x)) == π所有0 < x < 1
0xbadf00d 2016年

这是一个可怕的主意。使用每个类型的重载模板constexpr,这样会出现编译错误,如果出现新类型,则会强制您对其进行定义。由于trig类型不限于浮点类型,因此通常也很糟糕。因此,请享受atan(1)错误...该标准不能保证三角函数计算出其实际三角函数值的精度。它们通常不这样做,例如使用快速数学会变得更糟,并且对于特殊值总是特别不利。
Midjji

10

我在涵盖所有基础的项目中的一个常见标头之一中使用以下代码:

#define _USE_MATH_DEFINES
#include <cmath>

#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

#ifndef M_PIl
#define M_PIl (3.14159265358979323846264338327950288)
#endif

附带说明一下,如果包括在内,所有下面的编译器都定义M_PI和M_PIl常量<cmath>。无需添加VC ++才需要的#define _USE_MATH_DEFINES。

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

下降投票者能否评论这个答案有什么问题。这是经过充分研究和测试的,并已在实际系统中使用。如果出现问题,我绝对想改进它。
Shital Shah

1
仅供参考,用Borland C ++编译器也可以定义M_PI无需_USE_MATH_DEFINES
雷米勒博

8

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

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


8
acos(-1)也是pi。
罗德里克·泰勒

3
与从内存位置读取操作数相比,加载立即操作数通常需要更少的CPU指令和/或更少的延迟。另外,只能预先计算在编译时已知的表达式(我的意思double x = pi * 1.5;是类似的意思)。如果您打算在紧密循环中的易碎数学中使用PI,则最好确保编译器知道该值。
尤金·里亚布采夫

7

我刚看过Danny Kalev 撰写的这篇文章该文章对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>

4

像M_PI,M_PI_2,M_PI_4之类的值不是标准C ++,因此constexpr似乎是一个更好的解决方案。可以制定出不同的const表达式来计算相同的pi,这关系到我(是否)(全部)为我提供完整的准确性。C ++标准未明确提及如何计算pi。因此,我倾向于使用手动定义pi。我想分享下面的解决方案,该解决方案完全支持pi的所有分数。

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

2
非常好。该数字的末尾可能必须带有“ l”或“ L”。我从Linux上的编译器gcc收到警告警告。
Grant Rostig

2

在Windows(cygwin + g ++)上,我发现有必要-D_XOPEN_SOURCE=500为预处理器添加标志以处理M_PIin 的定义math.h


2
这不是答案,而是对fritzone答案的评论。
0xbadf00d 2016年

2
@ 0xbadf00d:这是一个完全独立的答案,提供了M_PI在特定平台上工作所需的步骤。那不再是对其他平台答案的评论,而对于其他平台的答案就是对这个平台的评论。
Ben Voigt

2

C ++ 14让您做到 static constexpr auto pi = acos(-1);


9
std::acos不是constexpr。因此,您的代码将无法编译。
0xbadf00d

@ 0xbadf00d我使用g ++进行了编译
Willy Goat

12
@WillyGoat:那么g ++是错误的,因为acos它不在constexprC ++ 14中,并且constexpr甚至没有提议在C ++ 17中变成
Ben Voigt

@BenVoigt有哪些数学函数constexpr?显然不是:stackoverflow.com/questions/17347935/constexpr-math-functions
wcochran

1
@wcochran:有很多新的数学函数constexpr,请参见例如(github.com/kthohr/gcem)。但是它们不与同名的C函数向后兼容,因此它们不能接管旧名称。
Ben Voigt

2

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

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

256个十进制数字精度对于任何将来的long long long double类型都应该足够。如果需要更多信息,请访问https://www.piday.org/million/



1

你可以这样做:

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

如果M_PI已在中定义cmath,则除了include之外不会做任何其他事情cmath。如果M_PI未定义(例如在Visual Studio中就是这种情况),它将对其进行定义。在这两种情况下,都可以M_PI用来获取pi的值。

pi的值来自Qt Creator的qmath.h。


1

您可以使用:

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.