在C/C++中定义PI的更好/更可移植的方法?

sxissh06  于 8个月前  发布在  C/C++
关注(0)|答案(2)|浏览(99)

目前,在我的一个科学计算代码中,我有以下代码:

//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++代码中,什么是最佳实践?

eni9jsuy

eni9jsuy1#

不要低估可读性问题;就我个人而言,我会这样做:

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

这可能不会有任何更多或更少的可移植性(atanM_PI在C和C++中都是标准的)。但是,它比使用atan更可读,并且根据编译器的优化设置,可能会为您节省保存昂贵的函数调用。

更新:看起来M_PI并不像我想象的那么标准。包含上面的#ifndef应该注意它不可用的情况。

pkbketx9

pkbketx92#

通用解决方案

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

#define PI 3.1415926535897932384626433832795028L
#define DEG_TO_RAD(...) ((__VA_ARGS__) * (PI / 180))
// note: write DEG_TO_RAD(x) in C89

这适用于任何版本的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 new numeric constants
#include <numbers>

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__)

相关问题