计算 pi

问题描述

我正在尝试自学来自 python/java 的 C(我认为是 C99?gcc 8.1.0)。我正在研究的练习题之一是如何将 pi 计算为给定的小数。

我目前使用以下等式 2 * (Arcsin(sqrt(1 - 0.5^2)) + abs(Arcsin(0.5))).

float pi_find(float nth)
{
    float x,y,z;

    /* Equation = 2 * (Arcsin(sqrt(1 - x^2)) + abs(Arcsin(x))) [x|-1<=x=>1,xeR]*/
    x = sqrt(1-pow(nth,2)); /* Carrot (^) notation does not work,use pow() */
    y = fabs(asin(nth)); /* abs is apparently int only,use fabs for floats */
    z = x+y;
    printf("x: %f\ny: %f\nsum: %f\n",x,(x+y));
    printf("%f\n",asin(z));
    return 2 * asin(z); /* <- Error Happens */
}
int main()
{
  float nth = 0.5f;

  double pi = pi_find(nth);

  printf("Pi: %f\n",pi);

  return 0;
}

结果:

x: 0.866025
y:0.523599
sum: 1.389624
z:-1.#IND00
Pi:-1.#IND00

我知道问题在于添加x + y,其总和为 1.389...而 asin() 只能处理 -1 和 +1 之间的值。

但是!

我使用 Wolfram Alpha 和 python 来检查每一步的计算是否正确,它可以计算 asin(1.389...)。 [1]

我不懂虚数数学,这远远超出了我作为数学家的能力,但以下是 Wolfram 正在做的事情。 [2]

1.570796 -0.8563436 i
Interpreting as: 0.8563436 i
Assuming multiplication | Use a list instead
Assuming i is the imaginary unit | Use i as a variable instead

在撰写本文时,我发现了 C99 中添加_Imaginary 数据类型,但我真的不明白它是否与 Wolfram 所做的相同。

还查了一下虚数是如何工作的,但我真的不明白'负数的平方根在两者之一被定义为虚数单位之前无法区分'是如何工作的. [3]

有人能帮我指点一下解决这个问题吗? 这显然是一个知识问题,而不是数学或语言限制

p.s 是的,我知道这是垃圾代码,我在正确重写之前使用了一种奇怪的调试方式。

[1]:Wolfram_Alpha Calculation [2]:Wolfram_Alpha Assumption [3]:Imaginary Numbers

解决方法

问题是您对表达式的分组不正确。所需的表达式是:

2 * (Arcsin(sqrt(1 - 0.5^2)) + abs(Arcsin(0.5)))

用 nth 代替 0.5,这变成:

2 * (Arcsin(sqrt(1 - nth^2)) + abs(Arcsin(nth))).

特别是,第一个 Arcsin 的参数是 sqrt(1 - nth^2)),而第二个 Arcsin 的参数是 nth。

您最好使用 nth * nth 而不是 pow(nth,2)。它更快更准确。

所以你想要的是:

x = asin(sqrt(1 - nth*nth));
y = fabs(asin(nth));
r = 2*(x + y);

请注意,asin 的参数永远不会具有大于 1 的量级(只要 nth 小于 1)。

此外,正如我之前在评论中提到的,您应该将所有 float 变量更改为 double。无论如何,您都在使用双精度数学库函数,因此没有理由通过将结果存储在 float 变量中来丢弃一半的精度。

,

在 C 中,floatdouble 类型模拟“实数”数字,我假设您已经掌握了。

在数学中,"complex" numbers 是实数的扩展。每个实数都算作复数,但“虚数”也是如此,您可以通过将实数乘以“虚数单位”(在数学符号中标记为 i,通常被描述为“平方-1"的根").

从数学上讲,基本算术运算(+-*/)是在复数上定义的。事实证明,您可以扩展像反正弦这样的函数来对复数进行运算。

无需深入了解细节,Wolfram Alpha 几乎肯定会从复杂版本的反正弦中为您提供值。

然而,标准 C 函数 asin() 是未扩展的版本:它接受一个 double 作为参数,并返回一个 double 作为结果。由于 double 仅对实数建模,因此 asin() 对于 [-1,1] 之外的输入值没有意义。