如何在一段时间内准确计算正弦波

Abr*_*hez 6 c algorithm audio trigonometry sound-synthesis

用例是为数字合成生成正弦波,因此,我们需要计算sin(dt)的所有值,其中:

t是整数,表示样本编号.这是可变的.对于CD质量的一小时声音,范围从0到158,760,000.

d是double,表示角度的增量.这是不变的.范围是:大于0,小于pi.

目标是使用传统的intdouble数据类型实现高精度.表现并不重要.

天真的实施是:

double next()
{
    t++;
    return sin( ((double) t) * (d) );
}
Run Code Online (Sandbox Code Playgroud)

但是,问题是当t增加时,精度会降低,因为大数字提供给"罪"功能.

改进版本如下:

double next()
{
    d_sum += d;
    if (d_sum >= (M_PI*2)) d_sum -= (M_PI*2);

    return sin(d_sum);
}
Run Code Online (Sandbox Code Playgroud)

在这里,我确保提供从0到2*pi到"sin"函数的数字.

但是,现在问题是当d很小时,会有许多小的增加,每次都会降低精度.

这里的问题是如何提高准确性.


附录1

"准确性降低,因为大数字提供给"罪"功能":

#include <stdio.h>
#include <math.h>

#define TEST      (300000006.7846112)
#define TEST_MOD  (0.0463259891528704262050786960234519968548937998410258872449766)
#define SIN_TEST  (0.0463094209176730795999323058165987662490610492247070175523420)

int main()
{
    double a = sin(TEST);
    double b = sin(TEST_MOD);

    printf("a=%0.20f \n" , a);
    printf("diff=%0.20f \n" , a - SIN_TEST);
    printf("b=%0.20f \n" , b);
    printf("diff=%0.20f \n" , b - SIN_TEST);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

a=0.04630944601888796475
diff=0.00000002510121488442
b=0.04630942091767308033
diff=0.00000000000000000000
Run Code Online (Sandbox Code Playgroud)

MBo*_*MBo 2

您可以尝试使用快速傅立叶变换的一些实现的方法。三角函数的值是根据先前的值和 delta 计算的。

Sin(A + d) = Sin(A) * Cos(d) + Cos(A) * Sin(d)
Run Code Online (Sandbox Code Playgroud)

这里我们也必须存储和更新余弦值,并存储常数(对于给定的增量)因子 Cos(d) 和 Sin(d)。

现在关于精度:小 d 的 cosine(d) 非常接近 1,因此存在精度损失的风险(数字中只有很少的有效数字,例如 0.99999987)。为了解决这个问题,我们可以将常数因子存储为

dc = Cos(d) - 1 =  - 2 * Sin(d/2)^2
ds = Sin(d) 
Run Code Online (Sandbox Code Playgroud)

使用另一个公式来更新当前值
(这里sa = Sin(A)为当前值,ca = Cos(A)为当前值)

ts = sa //remember last values
tc = ca
sa = sa * dc + ca * ds
ca = ca * dc - ts * ds
sa = sa + ts
ca = ca + tc
Run Code Online (Sandbox Code Playgroud)

PS 一些 FFT 实现会定期(每 K 步)通过 trig更新sa和值。ca避免误差累积的功能。

结果示例。以双打计算。

d=0.000125
800000000 iterations
finish angle 100000 radians

                             cos               sin
described method       -0.99936080743598  0.03574879796994 
Cos,Sin(100000)         -0.99936080743821  0.03574879797202
windows Calc           -0.9993608074382124518911354141448 
                            0.03574879797201650931647050069581           
Run Code Online (Sandbox Code Playgroud)