用C++创建正弦查找表

use*_*444 15 c++ lookup trigonometry pseudocode

如何在C++中重写以下伪代码?

real array sine_table[-1000..1000]
    for x from -1000 to 1000
        sine_table[x] := sine(pi * x / 1000)
Run Code Online (Sandbox Code Playgroud)

我需要创建一个sine_table查找表.

Ale*_*ore 30

您可以通过仅存储第一象限的值(即[0,pi/2]中的x)将表的大小减小到原始值的25%.

要做到这一点,您的查找例程只需要使用简单的trig标识将x的所有值映射到第一象限:

  • sin(x)= - sin(-x),从象限IV映射到I
  • sin(x)= sin(pi - x),从象限II映射到I

要从象限III映射到I,请应用两个身份,即sin(x)= - sin(pi + x)

此策略是否有帮助取决于您的情况下内存使用量的重要性.但是,为了避免在查找期间进行比较和减法或两次,存储四倍于所需的值似乎是浪费.

我第二个Jeremy建议测量构建表是否比使用std :: sin()更好.即使使用原始的大表,您也必须在每次表查找期间花费周期来将参数转换为最接近的pi/1000增量,并且您将在此过程中失去一些准确性.

如果你真的试图用速度来换算精度,你可以尝试仅使用泰勒级数展开的前几个项逼近sin()函数.

  • sin(x)= x - x ^ 3/3!+ x ^ 5/5!......,其中^表示提升到力量!代表阶乘.

当然,为了提高效率,您应该预先计算阶乘并利用x的较低幂来计算更高的幂,例如在计算x ^ 5时使用x ^ 3.

最后一点,上面截断的泰勒级数对于接近零的值更准确,因此在计算近似正弦之前仍然值得映射到第一或第四象限.

附录:基于两个观察结果还有一个潜在的改进:
1.如果你可以计算第一个八分圆[0,pi/4]中的正弦和余弦,你可以计算任何触发函数
.2.以零为中心的泰勒级数展开是更准确接近零

因此,如果您决定使用截断的泰勒级数,那么您可以通过映射到正弦或余弦来提高准确度(或使用更少的术语以获得相似的精度),以使用类似身份获得[0,pi/4]范围内的角度除了上面的那些之外,sin(x)= cos(pi/2-x)和cos(x)= sin(pi/2-x)(例如,如果你已经映射到x> pi/4第一象限.)

或者,如果您决定对正弦和余弦使用表查找,则可以使用两个较小的表来覆盖范围[0,pi/4],但代价是另一个可能的比较和减法查找以映射到较小的范围.然后,您可以为表使用更少的内存,或使用相同的内存,但提供更精细的粒度和准确性.

  • 此外,四分之一正弦表可用于通过类似的象限重新映射方法查找余弦. (4认同)
  • 第一个身份有一个错字,应该是`sin (-x) = -sin(x)` (2认同)

Mik*_*one 5

你会想要这个std::sin()功能<cmath>.


Svi*_*ack 5

long double sine_table[2001];
for (int index = 0; index < 2001; index++)
{
    sine_table[index] = std::sin(PI * (index - 1000) / 1000.0);
}
Run Code Online (Sandbox Code Playgroud)


val*_*ldo 5

还有一点:调用三角函数是昂贵的。如果您想为具有恒定步长的正弦准备查找表 - 您可以节省计算时间,但代价是一些潜在的精度损失。

考虑您的最小步骤是“a”。也就是说,您需要 sin(a)、sin(2a)、sin(3a)、...

然后你可以做以下的技巧:首先计算 sin(a) 和 cos(a)。然后对于每个连续的步骤使用以下三角等式:

  • sin([n+1] * a) = sin(n*a) * cos(a) + cos(n*a) * sin(a)
  • cos([n+1] * a) = cos(n*a) * cos(a) - sin(n*a) * sin(a)

这种方法的缺点是在此过程中会累积舍入误差。