我想在一些 C++ 程序中使用 PI 常数和三角函数。我得到三角函数include <math.h>
。但是,此头文件中似乎没有 PI 的定义。
如何在不手动定义的情况下获得 PI?
我想在一些 C++ 程序中使用 PI 常数和三角函数。我得到三角函数include <math.h>
。但是,此头文件中似乎没有 PI 的定义。
如何在不手动定义的情况下获得 PI?
在某些(尤其是较旧的)平台上(请参阅下面的评论),您可能需要
#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.
*/
然而:
在较新的平台上(至少在我的 64 位 Ubuntu 14.04 上)我不需要定义_USE_MATH_DEFINES
在(最近的)Linux 平台上,也有一些long double
值作为 GNU 扩展提供:
# define M_PIl 3.141592653589793238462643383279502884L /* pi */
Pi 可以计算为atan(1)*4
。您可以通过这种方式计算值并将其缓存。
您还可以使用 boost,它为请求的类型(即 float 与 double)定义了具有最大准确性的重要数学常量。
const double pi = boost::math::constants::pi<double>();
查看boost 文档以获取更多示例。
而是从芯片上的 FPU 单元获取它:
double get_PI()
{
double pi;
__asm
{
fldpi
fstp pi
}
return pi;
}
double PI = get_PI();
C++20std::numbers::pi
终于到了:http ://eel.is/c++draft/numbers
主文件
#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
编译并运行:
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。“Headers” [headers] 在表 [tab:cpp.library.headers] 中,
<math>
需要添加一个新的 header。[...]
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
我建议使用-D_USE_MATH_DEFINES
或/D_USE_MATH_DEFINES
取决于您的编译器。
这样您就可以放心,即使有人在您之前包含标头(并且没有#define),您仍然会拥有常量而不是一个晦涩的编译器错误,您需要很长时间才能找到它。
我建议您只输入 pi 到您需要的精度。这不会为您的执行增加计算时间,并且无需使用任何标题或#defines 即可移植。计算 acos 或 atan 总是比使用预先计算的值更昂贵。
const double PI =3.141592653589793238463;
const float PI_F=3.14159265358979f;
由于官方标准库没有定义常量 PI,因此您必须自己定义它。所以回答你的问题“我如何在不手动定义的情况下获得 PI?” 是“你没有——或者你依赖于一些特定于编译器的扩展。”。如果您不关心可移植性,您可以查看编译器的手册。
C++ 允许您编写
const double PI = std::atan(1.0)*4;
但是这个常量的初始化不能保证是静态的。然而,G++ 编译器将这些数学函数作为内在函数处理,并且能够在编译时计算这个常量表达式。
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
标准 C++ 没有 PI 常量。
许多 C++ 编译器将M_PI
in cmath
(或math.h
用于 C)定义为非标准扩展。您可能必须#define _USE_MATH_DEFINES
先看到它。
我会做
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
将float
是double
或long double
。因此,只需输入 的精度long double
,即
template<typename T>
T const pi = static_cast<T>(/* long double precision π */);
但是你真的知道未来标准中不会有新的浮点类型比 精度更高long double
吗?你没有。
这就是为什么第一个解决方案很漂亮。您可以确定该标准会为新类型重载三角函数。
请不要说在初始化时对三角函数的评估是性能损失。
我在涵盖所有基础的项目中的一个常见标题中使用了以下内容:
#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>
. 无需添加 `#define _USE_MATH_DEFINES,这仅在 VC++ 中需要。
x86 GCC 4.4+
ARM GCC 4.5+
x86 Clang 3.0+
我通常更喜欢定义自己的:const double PI = 2*acos(0.0);
因为并非所有实现都为您提供它。
这个函数是在运行时被调用还是在编译时被静态输出的问题通常不是问题,因为它无论如何只会发生一次。
我刚刚看到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>
一些优雅的解决方案。我怀疑三角函数的精度是否等于类型的精度。对于那些喜欢写一个常量值的人,这适用于 g++ :-
template<class T>
class X {
public:
static constexpr T PI = (T) 3.14159265358979323846264338327950288419\
71693993751058209749445923078164062862089986280348253421170679821480865132823066\
47093844609550582231725359408128481117450284102701938521105559644622948954930381\
964428810975665933446128475648233786783165271201909145648566923460;
...
}
对于任何未来的 long long long double 类型,256 个十进制数字的精度应该足够了。如果需要更多,请访问https://www.piday.org/million/。
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;
}
在 C++20 标准库中,π 被定义为std::numbers::pi_v
for float
, double
and long double
,例如
#include <numbers>
auto n = std::numbers::pi_v<float>;
并且可能专门用于用户定义的类型。
在 windows (cygwin + g++) 上,我发现有必要-D_XOPEN_SOURCE=500
为预处理器添加标志来处理M_PI
in的定义math.h
。
#include <cmath>
const long double pi = acos(-1.L);
C++14 让你做static constexpr auto pi = acos(-1);
你可以这样做:
#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。
你可以使用它:
#define _USE_MATH_DEFINES // for C++
#include <cmath>
#define _USE_MATH_DEFINES // for C
#include <math.h>
标准 C/C++ 中未定义数学常量。要使用它们,您必须先定义_USE_MATH_DEFINES
然后包含cmath
or math.h
。
自从大学(也许是高中)以来,我已经将 pi 记住了 11 位数字,所以这始终是我的首选方法:
#ifndef PI
#define PI 3.14159265359
#endif
小数点后 15 位让人类到达月球表面并返回。超出此范围的任何事情在规模上都是天文数字。实际上,您能否在较小的范围内进行测量?其他人花了几个月的时间计算到数万亿位数。除了进入记录簿之外,这没有用。
知道您可以将 pi 计算为任意长度,但 keep 是实用的。
我不喜欢#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)
毫无疑问,其他语言也可以用来做同样的事情。