问题描述
我正在尝试自学来自 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 中,float
和 double
类型模拟“实数”数字,我假设您已经掌握了。
在数学中,"complex" numbers 是实数的扩展。每个实数都算作复数,但“虚数”也是如此,您可以通过将实数乘以“虚数单位”(在数学符号中标记为 i
,通常被描述为“平方-1"的根").
从数学上讲,基本算术运算(+
、-
、*
、/
)是在复数上定义的。事实证明,您可以扩展像反正弦这样的函数来对复数进行运算。
无需深入了解细节,Wolfram Alpha 几乎肯定会从复杂版本的反正弦中为您提供值。
然而,标准 C 函数 asin()
是未扩展的版本:它接受一个 double
作为参数,并返回一个 double
作为结果。由于 double
仅对实数建模,因此 asin()
对于 [-1,1]
之外的输入值没有意义。