在 C/C++ 中定义 PI 的更好/更便携的方法?

问题描述 投票:0回答:2

目前在我的科学计算代码中我有以下内容:

//Include M_PI constant
#define _USE_MATH_DEFINES
//Needed for square root function.
#include <math.h>

这有效......至少在 Linux 上......我还没有在所有平台上的 C 编译器上测试它。然而,在调查一些较旧的 Fortran 代码时,我最近发现了这种在另一个代码(不是我自己的代码)中定义 pi 的看似聪明的方法:

<angle in deg.>*8.0d0 * datan(1.0d0) / 360.0d0

当然这在C语言中是完全可行的,对吧?所以我可以定义我的转换函数,例如:

inline double DegToRad(const double A)
{
   return A * atan(1.0) / 45.0;
}

哪种方法更便携?是否有任何数字(例如四舍五入)考虑因素值得使用一种方法而不是另一种方法?

我确实喜欢 M_PI 常量使代码更具可读性。当然,我可以很高兴地使用上述方法分配我自己的 PI 常数。

针对多个平台(Windows、Linux 等)的 C/C++ 代码的最佳实践是什么?

c++ c math constants angle
2个回答
6
投票

不要最小化可读性问题;就我个人而言,我会做这样的事情:

#ifndef M_PI
// Source: http://www.geom.uiuc.edu/~huberty/math5337/groupe/digits.html
#define M_PI 3.141592653589793238462643383279502884197169399375105820974944592307816406 
#endif

#define DEG_TO_RAD (M_PI/180.0)
#define RAD_TO_DEG (180.0/M_PI)

然后声明

inline double DegToRad(const double deg) {
   return deg * DEG_TO_RAD;
}

inline double RadToDeg(const double rad) {
   return rad * RAD_TO_DEG;
}

这可能不会有更多或更少的可移植性(

atan
M_PI
都是C和C++中的标准)。然而,它比使用
atan
更具可读性,并且根据编译器的优化设置,可能会为您节省昂贵的三角函数调用。

更新:看起来M_PI并不像我想象的那么标准。包含上面的

#ifndef
应该可以解决它不可用的情况。


0
投票

通用解决方案

一种可能的方法是使用宏:

#define PI 3.1415926535897932384626433832795028L
#define DEG_TO_RAD(...) ((__VA_ARGS__) * (PI / 180))

这适用于任何版本的 C 和 C++。这里使用的位数对于 binary128 浮点格式来说“完全”足够。 这很重要,因为 long double 可能是一个 128 位数字。

M_PI

宏不可移植,您应该避免使用它。 您也可以定义自己的宏,保证在所有平台上都存在。

非宏观 pi 定义

// C23 / C++11 compatible constants. // Ideally, make these inline in C++17 to avoid possibly ODR violations. // In C89/C++98, these could be declared as static const instead. constexpr long double pil = 3.1415926535897932384626433832795028L; constexpr double pi = (double) pil; constexpr float pif = (float) pi;

// C++17 solution using constrained variable template
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
inline constexpr T pi_v = T(3.1415926535897932384626433832795028L);

// note: this is unnecessary in C++20 thanks to std::numbers::pi in <numbers>
度换算弧度

// C++20 using constraints and the constants from the <numbers> header template <std::floating_point T> T deg_to_rad(T x) { return x * std::numbers::pi_v<T>; }

// C++17, see the variable template above
template <typename T, std::enable_if_t<std::is_floating_point_v<T, int> = 0>
T deg_to_rad(T x) {
    return x * pi_v<T>;
}
// C11 using generic selection
float deg_to_radf(float x) { return x * (pif / 180); }
// ...
#define deg_to_rad(...) _Generic((__VA_ARGS__), \
    float: deg_to_radf,                         \
    double: deg_to_rad,                         \
    long double: deg_to_radl)(__VA_ARGS__)
© www.soinside.com 2019 - 2024. All rights reserved.