我可以使用什么更准确的算法来计算数字的正弦值?
What is a more accurate algorithm I can use to calculate the sine of a number?
我有这段代码可以计算正弦的猜测值并将其与标准 C 库(在我的例子中是 glibc)的结果进行比较:
#include <stdio.h>
#include <math.h>
double double_sin(double a)
{
a -= (a*a*a)/6;
return a;
}
int main(void)
{
double clib_sin = sin(.13),
my_sin = double_sin(.13);
printf("%.16f\n%.16f\n%.16f\n", clib_sin, my_sin, clib_sin-my_sin);
return 0;
}
double_sin
的准确性很差(大约 5-6 位数)。这是我的输出:
0.1296341426196949
0.1296338333333333
0.0000003092863615
如您所见,在 .12963
之后,结果有所不同。
一些注意事项:
我认为泰勒级数不适用于这种特定情况,更高准确性所需的阶乘无法存储在 unsigned long long
.[=18= 中]
查找表不是一个选项,它们占用太多space并且通常不提供任何关于如何计算结果的信息。
如果你使用幻数,请解释一下(虽然我更愿意不使用它们)。
我非常希望算法易于理解并且能够用作参考,而不是其他算法。
结果不必非常准确。最低要求是 IEEE 754, C, and/or POSIX.
我用的是IEEE-754double
格式,可以放心
支持的范围至少需要从-2*M_PI
到2*M_PI
。如果包括范围缩小,那就太好了。
我可以使用哪种更准确的算法来计算数字的正弦值?
我有一个类似于 Newton-Raphson 的想法,但是用于计算正弦。 但是,我找不到任何关于它的东西,我排除了这种可能性。
您实际上可以非常接近泰勒级数。诀窍是不要在每次迭代时计算完整的阶乘。
泰勒级数是这样的:
sin(x) = x^1/1! - x^3/3! + x^5/5! - x^7/7!
查看这些项,您通过将分子乘以 x^2、将分母乘以阶乘中的下两个数字并交换符号来计算下一项。然后在添加下一项不会改变结果时停止。
所以你可以这样编码:
double double_sin(double x)
{
double result = 0;
double factor = x;
int i;
for (i=2; result+factor!=result; i+=2) {
result += factor;
factor *= -(x*x)/(i*(i+1));
}
return result;
}
我的输出:
0.1296341426196949
0.1296341426196949
-0.0000000000000000
编辑:
如果反向添加项,可以进一步提高准确性,但这意味着计算固定数量的项:
#define FACTORS 30
double double_sin(double x)
{
double result = 0;
double factor = x;
int i, j;
double factors[FACTORS];
for (i=2, j=0; j<FACTORS; i+=2, j++) {
factors[j] = factor;
factor *= -(x*x)/(i*(i+1));
}
for (j=FACTORS-1;j>=0;j--) {
result += factors[j];
}
return result;
}
如果 x
落在 0 到 2*PI 的范围之外,此实现将失去准确性。这可以通过在函数开始时调用 x = fmod(x, 2*M_PI);
来规范化值来解决。
我有这段代码可以计算正弦的猜测值并将其与标准 C 库(在我的例子中是 glibc)的结果进行比较:
#include <stdio.h>
#include <math.h>
double double_sin(double a)
{
a -= (a*a*a)/6;
return a;
}
int main(void)
{
double clib_sin = sin(.13),
my_sin = double_sin(.13);
printf("%.16f\n%.16f\n%.16f\n", clib_sin, my_sin, clib_sin-my_sin);
return 0;
}
double_sin
的准确性很差(大约 5-6 位数)。这是我的输出:
0.1296341426196949
0.1296338333333333
0.0000003092863615
如您所见,在 .12963
之后,结果有所不同。
一些注意事项:
我认为泰勒级数不适用于这种特定情况,更高准确性所需的阶乘无法存储在
unsigned long long
.[=18= 中]查找表不是一个选项,它们占用太多space并且通常不提供任何关于如何计算结果的信息。
如果你使用幻数,请解释一下(虽然我更愿意不使用它们)。
我非常希望算法易于理解并且能够用作参考,而不是其他算法。
结果不必非常准确。最低要求是 IEEE 754, C, and/or POSIX.
我用的是IEEE-754
double
格式,可以放心支持的范围至少需要从
-2*M_PI
到2*M_PI
。如果包括范围缩小,那就太好了。
我可以使用哪种更准确的算法来计算数字的正弦值?
我有一个类似于 Newton-Raphson 的想法,但是用于计算正弦。 但是,我找不到任何关于它的东西,我排除了这种可能性。
您实际上可以非常接近泰勒级数。诀窍是不要在每次迭代时计算完整的阶乘。
泰勒级数是这样的:
sin(x) = x^1/1! - x^3/3! + x^5/5! - x^7/7!
查看这些项,您通过将分子乘以 x^2、将分母乘以阶乘中的下两个数字并交换符号来计算下一项。然后在添加下一项不会改变结果时停止。
所以你可以这样编码:
double double_sin(double x)
{
double result = 0;
double factor = x;
int i;
for (i=2; result+factor!=result; i+=2) {
result += factor;
factor *= -(x*x)/(i*(i+1));
}
return result;
}
我的输出:
0.1296341426196949
0.1296341426196949
-0.0000000000000000
编辑:
如果反向添加项,可以进一步提高准确性,但这意味着计算固定数量的项:
#define FACTORS 30
double double_sin(double x)
{
double result = 0;
double factor = x;
int i, j;
double factors[FACTORS];
for (i=2, j=0; j<FACTORS; i+=2, j++) {
factors[j] = factor;
factor *= -(x*x)/(i*(i+1));
}
for (j=FACTORS-1;j>=0;j--) {
result += factors[j];
}
return result;
}
如果 x
落在 0 到 2*PI 的范围之外,此实现将失去准确性。这可以通过在函数开始时调用 x = fmod(x, 2*M_PI);
来规范化值来解决。