【问题标题】:How to use the PI constant in C++如何在 C++ 中使用 PI 常量
【发布时间】:2010-12-16 05:16:41
【问题描述】:

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

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

【问题讨论】:

标签: c++ trigonometry


【解决方案1】:

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

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

【讨论】:

  • #define _USE_MATH_DEFINES 后跟 #include &lt;math.h&gt; 在 Visual C++ 中定义 M_PI。谢谢。
  • 也适用于 cygwin 标头。
  • 您始终可以包含cmath 而不是math.h
  • 即使在定义 _USE_MATH_DEFINES 之后,如果 GCC 抱怨那是因为 __STRICT_ANSI__ 已定义(也许您传递了 -pedantic-std=c++11),这不允许定义 M_PI,因此用 @ 取消定义它987654342@。自己定义时,因为它是 C++,而不是宏,你应该constexpr auto M_PI = 3.14159265358979323846;
  • 截至 2018 年,答案应该明确更新为使用 而不是
【解决方案2】:

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

【讨论】:

  • 对于 c++11 用户:constexpr double pi() { return std::atan(1)*4; }
  • -1:仅在 atan(1)*4 == 3.141592653589793238462643383279502884 时有效(粗略地说)。我不会打赌。正常并使用原始文字来定义常量。为什么在不需要时会丢失精度?
  • atan2(0, -1); 可以避免乘法运算。
  • @matiu atan 不是 constexpr
  • 改用acos(-1),不需要atan2
【解决方案3】:

您还可以使用 boost,它为请求的类型(即 float 与 double)定义了具有最大准确性的重要数学常量。

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

查看boost documentation 了解更多示例。

【讨论】:

  • Boost:提升自 1999 年以来 C++ 已经不必要的复杂性!
  • 朗朗上口,部分正确。另一方面,提升有时会非常有用......
  • @DanMoulding:嗯。 C是你知道的唯一其他语言吗?因为我知道的所有其他语言,除了 C,都有一个比 C++ 大得多的标准库(例如 Python、Haskell、C#、PHP、Delphi、Erlang、Java ......)。从个人经验来看,精英not gonna use libs-opinion 是一个害虫,可能是用 C++ 编写的糟糕软件的首要原因。
  • @Gracchus:是的。没有库(或没有新的 C++11 库)的 C++,尽管我很喜欢这种语言,也很想自己编写所有代码,但效率并不高。
  • 我相信他说的是complexity而不是size。大概是指 a) 3 个嵌套的命名空间,b) 将 pi 定义为模板函数,而不仅仅是一个普通的常量。
【解决方案4】:

改为从片上 FPU 单元获取它:

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

double PI = get_PI();

【讨论】:

  • :-) 可能不是独立于平台的,而是一个不错的额外奇特解决方案!
  • 我喜欢你在这里开箱即用的方式;)
  • 我喜欢这个答案。它在针对较旧的 x86 平台时特别有用,这是最近的一个小时尚,优化编译器不像现代编译器那样复杂。感谢这个 Henrik!
【解决方案5】:

C++20 std::numbers::pi

终于到了: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

根据:How can I calculate pi using Bash command

编译运行:

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 上测试

接受的提案描述:

5.0。 “标题” [标题] 在表 [tab:cpp.library.headers] 中,需要添加一个新的&lt;math&gt; 表头。

[...]

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

当然还有std::numbers::e :-) How to calculate Euler constant or Euler powered in C++?

这些常量使用 C++14 变量模板功能:C++14 Variable Templates: what is their purpose? Any usage example?

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

【讨论】:

  • OMG 20 多年过去了,我们终于拥有了它。太棒了:)
  • 我不明白为什么长双精度 pi 的精度只比双精度 pi 多两位。这表明几乎不值得使用 long double。
  • @Anachronist 很好的观察,我不太清楚为什么会这样,如果有人弄清楚了,请告诉我,我现在懒得想了:) 相关:stackoverflow.com/questions/15176290/…跨度>
  • 为什么浮点值相差这么远? 3.xxxxxx74101257324219?应该是 3.14159265f
  • @J.Tully Google 建议 7 个数字对于 32 位浮点数是正确的:stackoverflow.com/questions/13542944/… 不过我并没有停下来思考自己。
【解决方案6】:

而不是写

#define _USE_MATH_DEFINES

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

这样您就可以放心,即使有人在您之前包含了标头(并且没有#define),您仍然会有常量而不是一个晦涩的编译器错误,您需要很长时间才能找到它。

【讨论】:

  • 好提示。如果“您”是一个编译单元,那么您当然可以确保在包含任何内容之前定义宏。但是如果“你”是一个头文件,那是你无法控制的。
  • 事实上,即使“你”是一个编译单元......取决于标题的顺序是维护噩梦的最短路径......
  • 不过,您不必依赖标题的顺序。标题是否相互包含并不重要,前提是您在 #include 之前执行 #define (至少,假设没有 #undefs 它)。同样适用于 NDEBUG。
  • 项目中非常常见的问题是,例如,如果您使用 Visual Studio 进行编译,您不知道编译器将按照哪个顺序遍历您的文件,因此如果您使用 @987654324 @ 在不同的地方它会变得很痛苦(特别是如果它被您包含的另一个库包含)。如果他们把那部分放在头后卫之外会好得多,但现在不能做太多了。编译器指令确实工作得很好。
【解决方案7】:

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

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

【讨论】:

  • 这是一个很好的例子,为什么我们不应该采用这种方法,我们人会犯错误,四舍五入,复制粘贴等。我认为使用 M_PI 是正确的方法。
  • 如果有人在 C++11 中执行此操作,请将 const 设为 constexpr
  • @nacho4d 如果 M_PI 可用,我也更喜欢它,但并非所有系统都符合 POSIX。对于 M_PI 不可用的情况,我认为这种方法比 4*atan(1) 方法更好。
  • “计算 acos 或 atan 总是更昂贵”是不正确的。任何现代优化编译器都了解标准数学函数,并且可以通过它们进行常量传播。参见例如goo.gl/BvdJyr
  • @Nemo,反例:godbolt.org/g/DsAern 正如其他地方所说,目前似乎只有 GCC 这样做,这可能是因为它已将基本数学函数声明为 constexpr
【解决方案8】:

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

C++允许你写

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

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

【讨论】:

  • 我通常使用 acos(-1),正如你所说,它们是编译时评估的。当我测试 M_PI、acos(-1) 和 atan(1)*4 时,我得到了相同的值。
  • 传统方式是使用4*atan(1.)atan很容易实现,乘以4是精确运算。当然,现代编译器以所需的精度折叠(旨在折叠)所有常量,使用 acos(-1) 甚至 std::abs(std::arg(std::complex&lt;double&gt;(-1.,0.))) 这是完全合理的,这是欧拉公式的倒数,因此比看起来更美观(I'我添加了abs,因为我不记得复平面是如何被切割的,或者根本不记得它是否被定义)。
  • 这样就不会有人意外地认为你是认真的(再次-_-')。这是一个糟糕的解决方案。 atan 实现未由标准定义,这意味着它的实现和可能依赖于硬件。这意味着数字可能很糟糕,这意味着通常使用 3.14 可能会更好。此外,即使在特殊情况下,它也很可能很慢。
【解决方案9】:

来自Posix man page of 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

【讨论】:

  • 很好的答案,但链接已失效。我建议改为this one
【解决方案10】:

标准 C++ 没有 PI 常量。

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

【讨论】:

    【解决方案11】:

    我愿意

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

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

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

    你可能会说:你在说什么? T 将是 floatdoublelong double。因此,只需输入long double 的精度,即

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

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

    这就是为什么第一个解决方案很漂亮。您可以确定该标准会为新类型重载三角函数。

    请不要说在初始化时评估三角函数会降低性能。

    【讨论】:

    • 请注意,arg(log(x)) == π 代表所有 0 &lt; x &lt; 1
    • 这是一个糟糕的主意。使用每个类型重载的模板 constexpr,这样你会得到一个编译错误来强制你在出现新类型时定义它。它通常也很糟糕,因为三角类型不限于浮点类型。所以享受 atan(1) 错误......该标准不保证三角函数计算其实际三角值以达到类型的准确性。他们通常不这样做,而且它会变得更糟,例如fastmath 并且对于特殊值总是特别糟糕。
    【解决方案12】:

    我在涵盖所有基础的项目中的一个常见标题中使用以下内容:

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

    附带说明,如果您包含&lt;cmath&gt;,以下所有编译器都会定义 M_PI 和 M_PIl 常量。无需添加 `#define _USE_MATH_DEFINES,仅 VC++ 需要。

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

    【讨论】:

    • 反对者能否评论这个答案有什么问题。这是经过充分研究和测试的,并且正在实际系统中使用。如果有问题,我肯定想改进它。
    • 仅供参考,Borland C++ 编译器也定义了M_PI,而不需要_USE_MATH_DEFINES
    【解决方案13】:

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

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

    【讨论】:

    • 与从内存位置读取操作数相比,加载立即操作数的 CPU 指令和/或延迟通常更少。此外,只有在编译时已知的表达式才能被预先计算(我的意思是double x = pi * 1.5; 等)。如果您打算在紧凑的循环中使用 PI 进行精确的数学运算,最好确保编译器知道该值。
    【解决方案14】:

    我刚刚遇到Danny Kalevthis article,它对 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>
    

    【讨论】:

    • 3.1415926535897932385 是一个 double 常量。将数字转换为 double 时会丢失准确性,而将 double 转换为 long double 会保持值不变,但会丢失准确性。
    【解决方案15】:

    一些优雅的解决方案。我怀疑三角函数的精度是否等于类型的精度。对于那些喜欢写一个常量值的人,这适用于 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/

    【讨论】:

      【解决方案16】:

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

      【讨论】:

      • 非常好。可能需要在该数字的末尾加上“l”或“L”。我从 linux 上的编译器 gcc 收到了一个缩小警告。
      【解决方案17】:

      在C++20标准库中,对于floatdoublelong doublee.g.,π定义为std::numbers::pi_v

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

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

      【讨论】:

        【解决方案18】:

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

        【讨论】:

        • 这不是答案,而是对fritzone答案的评论。
        • @0xbadf00d:这是一个完全独立的答案,提供了让M_PI 在特定平台上工作所需的步骤。这不再是对其他平台答案的评论,而对其他平台的答案是对这个平台的评论。
        【解决方案19】:
        #include <cmath>
        const long double pi = acos(-1.L);
        

        【讨论】:

        【解决方案20】:

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

        【讨论】:

        • std::acos 不是constexpr。所以,你的代码不会编译。
        • @0xbadf00d 我用g++编译的
        • @WillyGoat:那么g++就错了,因为acos在C++14中不是constexpr,甚至在C++17中也不建议变成constexpr
        • @BenVoigt 有没有 constexpr 的数学函数?显然不是:stackoverflow.com/questions/17347935/constexpr-math-functions
        • @wcochran:有很多新的数学函数是 constexpr,例如 (github.com/kthohr/gcem)。但是它们不向后兼容同名的 C 函数,因此它们不能接管旧名称。
        【解决方案21】:

        你可以这样做:

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

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

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

        【讨论】:

          【解决方案22】:

          你可以使用它:

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

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

          【讨论】:

            【解决方案23】:

            我从大学(也许是高中)起就记住了 11 位数字,所以这始终是我的首选方法:

            #ifndef PI
            #define PI 3.14159265359
            #endif
            

            【讨论】:

            • 这不是一个好的解决方案,因为标准头文件将以适合其 C++ 实现的精度定义 π。也就是说,他们将使用尽可能多的数字来尽可能准确地给出 π。他们还可以使用十六进制格式来避免从十进制到内部浮点格式的转换中的舍入问题。应该首选标准标题,如果要自己定义 π,它会包含更多数字。
            • “C++ 实现”究竟与 pi 的位数有什么关系?你指的是寄存器的位宽吗?
            • 这实际上是这个问题的唯一好答案。它不包含任何深奥的头文件,不运行任何进一步的三角运算数学,也不依赖于汇编代码。它只是使用老式的美国高中数学知识。赞一个,好先生!
            • @EricPostpischil:答案是什么,兄弟?
            • 我赞成,因为在用 C++ 编写 10 多年期间,我从未见过 pi 的定义,似乎 C++20 解决了这个问题。 “无需手动定义的解决方案。” - 很愚蠢,你可以为所欲为。而且我不确定 C 是否有责任从物理数学中定义任何常数。
            【解决方案24】:

            15 位小数让人类到达月球表面并返回。超出此范围的任何事情在规模上都是天文数字。实际上,您能否在较小的范围内进行测量?其他人花了几个月的时间计算到数万亿位数。除了进入记录簿之外,这没什么用。

            知道你可以将 pi 计算为任意长度,但保留是实用的。

            【讨论】:

            【解决方案25】:

            我不喜欢#defines,因为它们是简单的文本替换,类型安全性为零。如果省略括号,它们也可能导致使用表达式出现问题,例如

            #define T_PI 2*PI
            

            应该是

            #define T_PI (2*PI)
            

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

            namespace Constants {
                constexpr double PI = 3.141... ;
            }
            

            但是我没有对这些值进行硬编码(因为我也不喜欢这种方法),而是使用一个单独的 Fortran 程序来编写这个文件。我使用 Fortran 是因为它完全支持四精度(VisualStudio 上的 C++ 不支持)并且三角函数是 constexpr 的 C++ 等价函数。 例如

            real(8), parameter :: pi = 4*atan(1.0d0)
            

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-02-29
              • 2012-07-05
              • 2010-09-07
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多