为什么math.h定义了pi、pi/2和2/pi,但没有定义2*pi?

28
这是一个非常简单的问题:为什么有预定义常量的pi、pi/2、pi/4、1/pi和2/pi,但没有2*pi的预定义常量?这背后是否有更深层次的原因?
这个问题不涉及pi vs tau辩论的全部内容。我想知道实现某些常量而不是其他常量的技术原因。我能想到两种可能性:
1. 避免舍入误差。 2. 避免运行时除法,它可能更昂贵。

4
你会想要使用 2*pi 来做什么?大多数三角函数,例如说,"重复" 一般在 2*pi 后(具有一个 2*pi 的平移对称性)。更正式地说,例如,如果 a = b (mod 2*pi),那么 sin(a) = sin(b)。这使得通过在这些函数的参数中加上或减去 2*pi 是没有意义的。请注意,这不会改变函数的值。 - MvanGeest
9
有很多应用场景。首先考虑正态分布、快速傅里叶变换和柯西积分。 - hanno
3
为什么他们没有定义3*pi12*pi-4*pi?这个问题需要讨论,除非math.h的原始作者之一恰巧停下来回答它,否则没有确定性的答案。很抱歉,此问题被投票关闭,因为FAQ明确提到讨论和推测类型的问题不适合在这里进行设计。 - Ken White
3
@Ken:或者其他作者已经在其他地方回答了这个问题,有人可以找到那个答案并参考它。我真的不明白为什么人们认为标准理由问题是无法回答的——会议记录是(有点)详细的。 - Steve Jessop
7
我很关心这个,因为我每天大约要不停地输入这句话100次。 - hanno
显示剩余5条评论
2个回答

5

写2 * M_PI太难了吗?

说真的,很久以前,人们担心简单编译器可能无法执行常量折叠,而且除法成本过高,因此使用常量PI/2比冒运行时除法的风险更明智。 在我们现代的世界中,一个人可能只需定义M_PI并继续工作,但其他变体为了向后兼容性而存在。


@stevejessop:乘以2是精确的(除了溢出,这显然不会发生在这里) - Stephen Canon
@Dietrich:是的,我觉得我很蠢。 - Steve Jessop
1
(请注意,除以二也是精确的;M_PI_2也不会增加任何精度。) - Stephen Canon
嗯,tau 更容易书写。 - Friedrich -- Слава Україні
1
@stephen-canon -- 用有限精度表示的超越数乘以2不会得到精确的答案。 因此,有必要在某个地方定义实用常数tau=2*pi,以完全精度。 - George Rey
显示剩余4条评论

4

这只是我的猜测。

我认为这些常量与数学库中不同函数的实现相关:

ck@c:~/Codes/ref/glibc/math$ grep PI *.c
s_cacos.c:  __real__ res = (double) M_PI_2 - __real__ y;
s_cacosf.c:  __real__ res = (float) M_PI_2 - __real__ y;
s_cacosh.c:                    ? M_PI - M_PI_4 : M_PI_4)
...
s_clogf.c:      __imag__ result = signbit (__real__ x) ? M_PI : 0.0;
s_clogl.c:      __imag__ result = signbit (__real__ x) ? M_PIl : 0.0;
ck@c:~/Codes/ref/glibc/math$ 

M_PIM_PI_2M_PI_4经常出现,但没有2.0 * M_PI。所以对于Hanno最初的问题,我认为MvanGeest是正确的——在实现libm时,2π并不那么有用。

现在关于M_PI_2M_PI_4,它们的存在是有充分理由的。GNU C库的文档建议“这些常量来自Unix98标准,在4.4BSD中也可用”。那个时候编译器还不够智能。输入M_PI/4而不是M_PI_4可能会导致不必要的除法。虽然现代编译器可以优化掉这个问题(gcc从2008年开始使用mpfr,因此甚至可以正确地进行四舍五入),但使用数字常量仍然是编写高性能代码的更可移植的方法。


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