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

Eta*_*tan 437 c++ trigonometry

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

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

Fer*_*eak 502

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

#define _USE_MATH_DEFINES
Run Code Online (Sandbox Code Playgroud)

然后包含必要的头文件:

#include <math.h>
Run Code Online (Sandbox Code Playgroud)

pi的值可以通过以下方式访问:

M_PI
Run Code Online (Sandbox Code Playgroud)

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

# define M_PI           3.14159265358979323846  /* pi */
Run Code Online (Sandbox Code Playgroud)

但请检查你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.
 */
Run Code Online (Sandbox Code Playgroud)

然而:

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

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

    # define M_PIl          3.141592653589793238462643383279502884L /* pi */
    
    Run Code Online (Sandbox Code Playgroud)

  • `#define _USE_MATH_DEFINES`后跟`#include <math.h>`在visual c ++中定义`M_PI`.谢谢. (48认同)
  • 你总是可以包含`cmath`而不是`math.h`. (20认同)
  • 即使在定义了`_USE_MATH_DEFINES`之后,如果GCC抱怨是因为`__STRICT_ANSI__`被定义(也许你通过`-pedantic`或`-std = c ++ 11`),它不允许定义`M_PI`,因此用` - 取消定义它 - D__STRICT_ANSI__`.当你自己定义它时,因为它是C++,而不是宏,你应该`constexpr auto M_PI = 3.14159265358979323846;`. (9认同)
  • 截至 2018 年,答案应该明确更新为使用 &lt;cmath&gt; 而不是 &lt;math.h&gt; (6认同)
  • 也适用于cygwin标头. (3认同)
  • @Kapten-N M_1_PI 是 `1/pi`,而不是 `pi` (2认同)

Kon*_*man 164

Pi可以计算为atan(1)*4.你可以用这种方式计算值并缓存它.

  • 对于c ++ 11用户:`constexpr double pi(){return std :: atan(1)*4; }` (76认同)
  • 请尝试`acos(-1)`,不需要`atan2`. (40认同)
  • @matiu`atan`不是'constexpr`. (38认同)
  • -1:仅当`atan(1)*4 == 3.141592653589793238462643383279502884`(粗略地说)时才有效.我不打赌它.正常并使用原始文字来定义常量.为什么在不需要时会失去精确度? (36认同)
  • 可以使用`atan2(0,-1);`来避免乘法运算. (29认同)
  • 在人们开始厌倦C++ 11之前,`const auto Pi = atan(1.0)*4.0;`是什么问题?新功能有它们的位置,但前提是它们可以改进已有的功能. (5认同)
  • @ThomasEding - 我的`math.h`文件读取`#define M_PI 3.14159265358979323846`(如果是严格的ANSI). (4认同)
  • @ThomasEding它真的应该.特别是acos(-1)应该给pi最接近的值,因为-1是acos的特例.有趣的事实:让M_PI = pi-delta,(即delta是舍入误差),然后库调用sin(M_PI)应返回delta,精度为double.即罪(M_PI)+ M_PI,两者恰当地使用合适的方法加在一起,应该给你带有100+有效位的pi - 如果'sin'正确完成的话.我用glibc尝试了这个并且它有效. (3认同)
  • 使用Visual Studio社区,我使用constexpr收到错误. (3认同)
  • @WillyGoat你正在证明我关于使用“just”的私人规则:`acos`与`atan`一样非constexpr,所以你的建议不起作用。 (3认同)
  • @Pharap几乎没有:唯一的区别是使用`constexpr`强制在编译时计算值,而旧`const`在程序初始化时执行. (2认同)
  • @greggo:您使用的数据类型有 100 多个有效位?“正确完成”是否意味着计算出准确的值,没有任何数值误差? (2认同)
  • @HelloGoodbye 特别是,我检查它的方法是获取 python 浮点数(64 位双精度数)中的值,使用 `val.as_integer_ratio()` 将它们转换为精确的有理值,找到精确的有理和,然后打印使用整数 div 和 mod 等将十进制数输出到所需的位置;然后与发布的 pi 值进行比较。更简单的方法:您可以使用 "%36.34f" 打印两个原始双打,然后将它们添加到纸上:3.1415926535897931159979634685441852 + 0.00000000000000012246467321047 (2认同)
  • C++14,只需执行 `static constexpr auto pi = acos(-1);` 或仅执行 `M_PI` (2认同)

Bus*_*icK 108

您还可以使用boost,它为所请求的类型定义了具有最大精度的重要数学常量(即float vs double).

const double pi = boost::math::constants::pi<double>();
Run Code Online (Sandbox Code Playgroud)

查看增强文档以获取更多示例.

  • 提升:自1999年以来提升已经不必要的C++复杂性! (171认同)
  • @DanMoulding:嗯.C是你知道的唯一其他语言吗?因为除了C之外,我所知道的所有其他语言都有一个比C++更大的标准库(例如Python,Haskell,C#,PHP,Delphi,Erlang,Java ......).从个人经验来看,精英主义者"不会使用libs" - 意见是一种害虫,可能是用C++编写的糟糕软件的首要原因. (54认同)
  • Catchy和部分真实.另一方面,提升有时可以非常有用...... (43认同)
  • 我相信他说*复杂性*不是*尺寸*.大概是指a)3个嵌套的命名空间,以及b)将pi定义为模板化函数而不仅仅是常规常量. (11认同)
  • @Gracchus:是的.没有库(或没有新的C++ 11库)的C++,就像我喜欢那种语言一样,我想自己编写所有内容,而不是非常高效. (9认同)
  • @phresnel非常认真,提升是c ++可以像php一样容易编写的唯一原因,它具有相当大的相对性能提升,更不用说它的子代:json-spirit和websocket ++. (7认同)
  • 为什么几乎每个C++问题都有一个提升答案?Boost应该是另一种语言,否则它会污染C++ (4认同)
  • @DanMoulding: boost::math::constants 的要点是它们是*任意精度*常量。它们可用于通常需要四精度的气候规范。 (2认同)

小智 74

从芯片上的FPU单元获取它:

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

double PI = get_PI();
Run Code Online (Sandbox Code Playgroud)

  • :-)可能不是那个平台独立,但一个很好的额外的异国情调解决方案! (35认同)
  • 我喜欢这个答案。当针对较旧的 x86 平台时,它特别有用,这是最近的一个小时尚,其中优化编译器不像现代编译器那么复杂。谢谢你,亨利克! (3认同)
  • 我喜欢你在开箱即用的方式;) (2认同)
  • @user16217248-OnStrike 因为您不一定在运行的同一台机器上进行编译。也许运行时机器有一个更美味的 pi。 (2认同)

小智 47

我建议只需输入pi即可获得所需的精度.这将不会为您的执行增加计算时间,并且它可以在不使用任何标头或#defines的情况下移植.计算acos或atan总是比使用预先计算的值更昂贵.

const double PI  =3.141592653589793238463;
const float  PI_F=3.14159265358979f;
Run Code Online (Sandbox Code Playgroud)

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

Mat*_* M. 45

而不是写作

#define _USE_MATH_DEFINES
Run Code Online (Sandbox Code Playgroud)

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

通过这种方式,您可以确保即使在您执行之前包含标题的人(并且没有#define),您仍将拥有常量,而不是一个模糊的编译器错误,您需要花费很长时间才能跟踪.

  • 实际上即使"你"是一个编译单元......根据标题的顺序是维护噩梦的最短途径...... (3认同)
  • 项目中非常常见的问题是,如果您使用 Visual Studio 进行编译,您不知道编译器将按什么顺序遍历您的文件,因此如果您在不同的地方使用 `&lt;cmath&gt;` ,它就会变成一个很大的痛苦(特别是如果它包含在您所包含的另一个库中)。如果他们把那部分放在头球护罩之外会好得多,但现在我们对此无能为力。编译器指令确实工作得很好。 (2认同)

sel*_*tze 40

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

C++允许你写

const double PI = std::atan(1.0)*4;
Run Code Online (Sandbox Code Playgroud)

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

  • 我通常使用acos(-1),正如你所说,它们是编译时评估的.当我测试M_PI,acos(-1)和atan(1)*4时,我得到了相同的值. (6认同)
  • 传统的方法是使用 `4*atan(1.)`:`atan` 很容易实现并且乘以 4 是一个精确的操作。当然,现代编译器以所需的精度折叠(旨在折叠)所有常量,使用 `acos(-1)` 甚至 `std::abs(std::arg(std::complex&lt;double) 是完全合理的&gt;(-1.,0.)))`,它是欧拉公式的倒数,因此比看起来更美观(我添加了 `abs` 因为我不记得复平面是如何切割的,或者如果那是完全定义)。 (2认同)

小智 30

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
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案,但链接已死。我建议 [this one](https://www.unix.com/man-page/posix/7posix/math.h/) 代替。 (4认同)

Ric*_*dle 26

标准C++没有PI的常量.

许多C++编译器定义M_PIcmath(或在math.h为C)作为非标准扩展.您可能必须#define _USE_MATH_DEFINES先看到它.


0xb*_*00d 15

我会做

template<typename T>
T const pi = std::acos(-T(1));
Run Code Online (Sandbox Code Playgroud)

要么

template<typename T>
T const pi = std::arg(-std::log(T(2)));
Run Code Online (Sandbox Code Playgroud)

不会 输入π到你需要的精度.那甚至应该是什么意思?在你需要的精度是精度T,但我们并不知道T.

你可能会说:你在什么?T将是float,doublelong double.所以,只需键入精度long double,即

template<typename T>
T const pi = static_cast<T>(/* long double precision ? */);
Run Code Online (Sandbox Code Playgroud)

但是你真的知道未来标准中不会有新的浮点类型,其精度甚至高于long double?你没有.

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

并且,请不要说在初始化时对三角函数的评估是性能损失.

  • 这是一个糟糕的主意。使用每个类型重载的模板 constexpr,这样,如果出现新类型,您会收到编译错误,强制您定义它。它通常也很糟糕,因为三角函数类型不限于浮点类型。因此,请享受 atan(1) 错误......该标准不保证三角函数计算其实际三角值的类型的准确性。他们通常不会,并且对于例如快速数学来说情况会变得更糟,并且对于特殊值总是特别糟糕。 (2认同)

Joh*_*ane 12

在 C++20 标准库中,\xcf\x80 定义std::numbers::pi_vfloat,doublelong double例如

\n
#include <numbers>\nauto n = std::numbers::pi_v<float>;\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n


Cir*_*四事件 11

C ++ 20 std::numbers::pi

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

我希望用法是这样的:

#include <numbers>
#include <iostream>

int main() {
    std::cout << std::numbers::pi << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

当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>;
Run Code Online (Sandbox Code Playgroud)

std::numbers::e当然也有一个:-) 如何在C ++中计算欧拉常数或欧拉函数?

这些常量使用C ++ 14变量模板功能:C ++ 14变量模板:它们的用途是什么?有用法示例吗?

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

  • 天哪,20 多年过去了,我们终于拥有了它。惊人的 :) (8认同)
  • @Anachronist这可能是一个迹象,在g++10上,在你的机器上(就像在我的机器上),长双精度被实现为80位扩展精度,而不是四倍精度。 (2认同)

Sum*_*ndo 8

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

是否在运行时调用此函数或在编译时静态调出此函数的问题通常不是问题,因为它只会发生一次.

  • acos(-1)也是pi. (8认同)
  • 加载立即数操作数通常比从内存位置读取操作数更少的CPU指令和/或更少的延迟.此外,只能预编译在编译时已知的表达式(我的意思是`double x = pi*1.5;`等等).如果您打算在紧密循环中使用紧凑数学中的PI,则最好确保编译器知道该值. (3认同)

Shi*_*hah 8

我在项目中的一个常见标题中使用了以下内容:

#define _USE_MATH_DEFINES
#include <cmath>

#ifndef M_PI
#define M_PI (3.14159265358979323846)
#endif

#ifndef M_PIl
#define M_PIl (3.14159265358979323846264338327950288)
#endif
Run Code Online (Sandbox Code Playgroud)

另外,如果包含,下面的所有编译器都会定义M_PI和M_PIl常量<cmath>.不需要添加`#define _USE_MATH_DEFINES,这只是VC++所必需的.

x86 GCC 4.4+
ARM GCC 4.5+
x86 Clang 3.0+
Run Code Online (Sandbox Code Playgroud)


小智 7

我刚刚看到Danny Kalev 撰写的这篇文章,它对C++ 14及以上版本提出了很好的建议.

template<typename T>
constexpr T pi = T(3.1415926535897932385);
Run Code Online (Sandbox Code Playgroud)

我觉得这很酷(虽然我会尽可能使用最高精度的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>
Run Code Online (Sandbox Code Playgroud)


Jon*_*ton 5

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

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

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


归档时间:

查看次数:

984029 次

最近记录:

6 年,2 月 前