为什么存在M_PI_2、M_PI_4、M_1_PI和M_2_PI?

8

我理解为什么我们需要在标准头文件中包含这个:

#define M_PI       3.14159265358979323846   // pi

然而,我并没有看到拥有这些东西的太多好处:

#define M_PI_2     1.57079632679489661923   // pi/2
#define M_PI_4     0.785398163397448309616  // pi/4
#define M_1_PI     0.318309886183790671538  // 1/pi
#define M_2_PI     0.636619772367581343076  // 2/pi

在实际代码中,与使用M_PI/2M_PI/41/M_PI2/M_PI相比,这些的使用是否有优势?(在2020年及以后)拼出来的表达式难道不更易读吗?
我问这个问题主要是出于两个原因。首先,有一天我不小心混淆了M_PI_2M_2_PI(甚至还可能是2 * M_PI),花费了一段时间才发现有问题,然后又花费了一段时间才找出根本原因。如果只是阅读使用它们的代码而没有看到定义,仍然认为这不是非常明显的M_PI_2M_2_PI表示什么,那么使用这些定义实际上是一种降低代码可读性的反模式吗?
其次,在Windows(Visual C++)中,拥有这些定义仍然可能存在问题。与其定义所有这些变量,我更喜欢定义只有M_PI,然后在代码中说M_PI/2而不是M_PI_2。我有什么遗漏吗?

3
如果你用算术方法计算它们,会失去精度。 - Barmar
2
这些常量都不是C或C++标准的一部分。相反,它们属于POSIX标准。对于C ++,从C ++ 20开始,这些常量将在std :: numbers中标准化(至少是pi和1/pi)。 - walnut
1个回答

10
因为2和4是2的幂,所以M_PI_2M_PI_4实际上是多余的,与M_PI/2M_PI/4完全等效。然而,M_1_PI不一定等同于1/M_PI;后者有两个舍入(对pi的近似,然后是不精确的除法),而不仅仅是一个(对1/pi的近似)。

M_PI_4 != M_PI / 4, M_PI / 4 == 0.785398163397448309615 - Alan Birtles
1
如果FLT_RADIX为2,则M_PI_2和M_PI_4是冗余的。但它不一定要为2,并且在创建它们时可能更多地考虑了其他基数的普及程度。 - Eric Postpischil
2
在任何实现中,如果 FLT_RADIX == 2,则 M_PI_4 的解析值应等于 M_PI / 4 的解析值,即使用于定义它们的数字串不满足该属性。换句话说,它们可以使用一些十进制数字串来定义,这些数字串足以指定所需的二进制浮点值,但这两个字符串可能不是其十进制数比为 4:1 的字符串。 - Eric Postpischil
@AlanBirtles:就像Eric所说的那样。你必须查看它们舍入到的实际浮点值,而不是十进制字符串,以检查相等性。 - R.. GitHub STOP HELPING ICE
3
这个答案中的推理也被用于提议在std::numbers中引入数学常数到C++20中,以不包含M_PI_2M_PI_4的等效物,详情请参阅P0631 - walnut
1
M_2_PI是否也是多余的,因为它等于2*M_1_PI?我认为我们最好不要使用M_2_PI,因为它看起来非常容易被误解(和误用)为2π。 - flarn2006

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接