怎么用代码实现正弦值计算


泰勒展开

根据泰勒展开, 我们都知道:

sinx=xx33!+x55!x77!++(1)nx2n+1(2n+1)!+

只取前几项的话

f(x)=xx36+x5120

发现 f(x) 从 [0, π/2] 的值和 sin(x) 的值差的也不是很多嘛~(我的要求没那么高):

就很好办辣~

Coding

先来第一步取模, 把自变量的取值缩小到 [0, 2π), 再缩到 [0, π], 接着是 [0, π/2]. 现在就可以用 f(x) 计算近似的正弦值啦.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* my sin(x) */
#define myPI 3.1415926
// 这个实现后文我会称作"咱的 f(x)"
double f(double x) {
    // to [0, 2π)
    double twopi = 2.0 * myPI;
    x = fmod(x, twopi);
    if (x < 0) x += twopi;

    // to [0, π/2]
    int sign = 1;
    if (x >= myPI) {
        x -= myPI;
        sign = -1;
    }
    if (x > 0.5 * myPI) {
        x = myPI - x;
    }
    // now x is all in [-π/2, π/2]
    double x2 = x * x; // x^2
    double x3 = x2 * x; // x^3
    double x5 = x2 * x3; // x^5
    return sign * (x-x3/6+x5/120);
}

我测试了一下, 精度还是能用的, 当 x=π/2 时, 有 f(x)-sin(x) 的最大值≈ 0.00452485553.
还行

编程语言实现中的正弦函数具体写法

很多编程语言标准库中都是包含 sin(x) 函数的, 当然, 它肯定不能像咱的 f(x) 写的这么糙, 来瞅眼

glibc

接下来我会用"输入"这个字眼来代替 sin(x) 的自变量的值.

glibc 中的 sin() 实现可以在这里查看, 函数入口是这个文件中的 __sin() 函数.
不得不说还得是 glibc, 它在不同输入范围时, 会同时确保精度和速度, 使用不同的方法计算(当然, 核心还是泰勒展开).
先介绍一些 __sin() 所调用的函数:


下面正式介绍一下 __sin(), 它所做的主要是判范围, 选方法, 去用:

我们下面所说的"大小"指输入的绝对值大小

CPython

给 C 语言的 sin() 函数套了个壳, 包括其他很多数学函数也是这样干的
(最初我还以为它会自己实现一个呢, 不过我很理解)
可以到这里来看, 直接套了宏

参见