计算 pi 时 asin() 问题
Issue with asin() while calculating 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, y, (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 之间的值。
然而!
我在 python 旁边使用 Wolfram Alpha 检查每一步的计算是否正确,它可以计算 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 所做的相同。
也查了一下虚数是怎么算的,但是我不太明白'The square roots of a negative number cannot be distinguished until one of the two is defined as the imaginary unit'是怎么算的。 [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)))
用第 n 个代替 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
作为参数,并且 returns 一个 double
作为结果.由于 double
仅对实数建模,因此 asin()
对于 [-1,1]
.
之外的输入值没有意义
我正在尝试自学来自 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, y, (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 之间的值。
然而!
我在 python 旁边使用 Wolfram Alpha 检查每一步的计算是否正确,它可以计算 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 所做的相同。
也查了一下虚数是怎么算的,但是我不太明白'The square roots of a negative number cannot be distinguished until one of the two is defined as the imaginary unit'是怎么算的。 [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)))
用第 n 个代替 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
作为参数,并且 returns 一个 double
作为结果.由于 double
仅对实数建模,因此 asin()
对于 [-1,1]
.