C中的数学常数PI值


78

计算PI值是复杂的问题之一,维基百科谈到了 近似值了为此所做处理,并说很难准确计算PI。

C如何计算PI?它是每次计算还是使用不那么精确的固定值?


25
使用仅计算出小数点后39位的pi,就可以计算整个宇宙的周长,其精确度小于氢原子的直径。16位小数点(大约double等于)可以计算出太阳系的直径,其误差小于头发宽度。
pmg 2012年

我在网上找到了它,并发现它很有趣:crypto.stanford.edu/pbc/notes/pi/code.html
Francis Cugler

Answers:


91

在C中,Pi在math.h中定义: #define M_PI 3.14159265358979323846


2
该值是双精度值可用的最准确的表示形式。
长颈鹿队长2012年

36
不太完全-实际上,可能没有PI在C中定义一致的C实现<math.h>。POSIX指定M_PI,但是同样,符合C的实现可能未定义它。(POSIX提出了一些与C标准冲突的要求。)但是您可以在自己的程序中以这种方式定义它。
基思·汤普森

2
所以它是固定值,没有更高的精度了吗?
user1298016 2012年

6
附加信息:如果您使用M_PI并得到未定义的错误-可以通过#define _USE_MATH_DEFINES
spin_eight 2014年

2
@Danijel M代表“数学”。过去,所有“数学常数”都以开头M_。也有一样的东西M_EM_LN10等他们从未做了它对标准。
伦丁

29

C对应用程序直接可见的方式与“计算π”最接近的事情是 acos(-1)或类似。对于要计算的函数(使用C或FPU微代码),几乎总是使用多项式/有理数逼近来完成。

然而,一个有趣的问题是,计算三角函数(sincos,和tan)要求减少他们的说法模为2π的。由于2π不是绝对的有理数(甚至不是有理数),因此它不能以任何浮点类型表示,因此使用任何近似值都会导致大自变量的灾难性错误累积(例如,xis1e122*M_PI与2π不同乘以ε,则fmod(x,2*M_PI)与2π的正确值之差最多为mod2π的正确值的1e12 *ε/π倍x,也就是说,这完全没有意义。

C的标准数学库的正确实现只是在其源代码中包含一个巨大的π硬编码的超高精度表示形式,以处理正确的参数归约问题(并使用一些花哨的技巧使它变得不那么庞大) )。这是大多数/所有C版本的sin/ cos/tan函数的工作方式。但是,已知某些实现(例如glibc)在某些cpus(例如x86)上使用汇编实现,并且未执行正确的参数约简,导致完全无意义的输出。(顺便说一句,不正确的asm通常与小参数的正确C代码以大约相同的速度运行。)


2
我一直在寻找有关Trig函数的ISO C,附件F的参考资料,但是显然并没有对它们成为IEEE trig函数的硬性要求(这将要求它们对所有输入都正确且正确地取整)。因此,与其说是正确性,不如说是QoI(实施质量)问题。但是基于fdlibm的实现具有可在整个可表示值域使用的参数减少功能。
R .. GitHub停止帮助ICE

1
好的,我理解您在说什么,并感谢您提供指向fdlibm的指针。我的想法是,计算如此大的正弦x几乎是没有意义的,并且很可能是使用正弦函数的代码中的错误。如果x太大而变成整数,则x每个正弦周期只能表示6个值。当然,可以找到y满足条件的a,这很好,但是我怀疑在计算整数的正弦值方面是否有有用的应用。
弗里茨

1
与整数的极端情况相比,更有趣的x是分析此“ε误差”随着增大的速度x。如果出现重大错误1000*M_PI,则表示我理解该问题,并衷心同意您的意见。
弗里茨

2
@Fritz:根据我的回答中的粗略分析,fmod(x,2*M_PI)vs正确的参数减少值的误差随的幅度线性增长x。因此,假设x单位圆外刚好为1ulp ,您将为看到类似1000ulp的值1000*M_PI。零点sin附近的线性与斜率1接近,因此参数中的ulp数直接转换为结果中的ulp。
R .. GitHub停止帮助ICE

1
顺便说一句,sin(2^n)对于较大的值有有趣的应用n; 我不记得他们马上要做什么。
R .. GitHub STOP HELPING ICE

22

只需定义:

它应该为您提供数学函数正在使用的确切PI编号。因此,如果他们更改正切线,余弦或正弦值的PI值,则您的程序应始终保持最新;)


10
每当使用该常数时,这将评估非常昂贵的功能(acos)。几乎没有有效和合理的方法。
米尔·Adamec

3
@JaromírAdamec实际上,任何好的编译器都应将该表达式优化为常量,因为它是标准库中使用常量参数调用的纯函数;除非您不进行优化就编译。
UnrealEagle

2
相反,@JaromírAdamec,* C标准明确允许-甚至认可-编译器知道所有标准库函数的复杂细节!
安蒂·哈帕拉

1
@JaromírAdamec我没有说任何有关单个实现的质量的问题。我只是说,一个符合ISO 9899中描述的C规范的C编译器被明确允许替换/内联/替换同一个标准中描述的对标准库的所有引用,假定一个具有相同名称的函数将具有与标准中定义的行为相同的行为。存在于各种编译器中的开关仅是为了使非严格符合标准的程序在这些现代实现中正确编译。
Antti Haapala

1
acos函数是C语言本身的一部分。
卡兹(Kaz)

8

无论如何,您都没有无限的精度,因此C可以这样定义一个常数:

导入math.h来使用它


这意味着我可以设法获得比C定义更高的PI准确性。如果我使用双倍x = 1345 * PI; 但这受到程序中使用的double变量精度的限制吗???那意味着新定义的准确值是没有用的?
user1298016 2012年

是的:D ...“ double x = 1345 * PI”通过这项工作,您会失去一些准确性,因为PI尽可能准确。如果需要更高的准确性,则应实现自己的OWN结构并将结果存储在数组中(类似于Java中的BigInteger)。好?
Maziar Aboualizadehbehbahani 2012年

1
实际上M_PI不在C中。它是POSIX中XSI扩展选项的一部分。
R .. GitHub停止帮助ICE

@R ..,您确定它仅XSI吗?该文档没有他们对XSI,只有东西正常[XSI]标记(例如见)。
马修·弗拉申

2
啊,您在看SUSv2,它不是POSIX。这是在UNIX / POSIX规范合并之前,当时整个SUS是XSI。从SUSv3开始,Unix和POSIX标准进行了合并,其中XSI选项是Unix的一部分,被认为没有足够的用处或通用性,无法强制所有POSIX系统支持。后来在SUSv4(POSIX 2008)中,许多更有用的XSI选项被移至基本(POSIX)标准,而那些不太有用的选项逐渐被标记为过时,因此最终XSI选项将不再具有存在...
R .. GitHub停止帮助ICE


-2

我不知道究竟是如何C计算PI直接作为我更熟悉的C++不是C; 但是,您可以具有预定义的内容C macroconst例如:

或者您可以使用以下两个公式之一进行计算:

恕我直言,我不确定100%,但我认为atan()比便宜acos()


@RyanHaining这是我的错字,谢谢您指出。我做了适当的更正。
弗朗西斯·库格勒

1
它仍然无效C
Ryan Haining

@RyanHaining Hmm好吧;我知道您可以用C ++做到这一点,但我不确定C是否允许这样做。自从我从事C语言以来已经有很多年了。#define CONSTANT numberHere
弗朗西斯·库格勒

“对于具有静态或线程存储持续时间的对象,初始化器中的所有表达式均应为常量表达式或字符串文字”(C2011,6.7.9 / 4)。对我来说,尚不清楚您希望在不依赖宏的替代方案中希望具有哪些特征,但这是您要面对的主要问题之一。
John Bollinger

@JohnBollinger很有道理;我的C很生锈,因为我从事这项工作已经有20年之久了,而且我经常使用它C++。我本来是C++从一个角度回答这个问题的...我已经更新了答案以适应这个问题……
Francis Cugler
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.