在 C++ 中创建正弦查找表

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3688649/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:28:35  来源:igfitidea点击:

Create sine lookup table in C++

c++lookuppseudocodetrigonometry

提问by user466444

How can I rewrite the following pseudocode in C++?

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

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

I need to create a sine_table lookup table.

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

回答by Alex Blakemore

You can reduce the size of your table to 25% of the original by only storing values for the first quadrant, i.e. for x in [0,pi/2].

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

To do that your lookup routine just needs to map all values of x to the first quadrant using simple trig identities:

为此,您的查找例程只需使用简单的三角标识将 x 的所有值映射到第一象限:

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

To map from quadrant III to I, apply both identities, i.e. sin(x) = - sin (pi + x)

要从象限 III 映射到 I,应用两个恒等式,即 sin(x) = - sin (pi + x)

Whether this strategy helps depends on how much memory usage matters in your case. But it seems wasteful to store four times as many values as you need just to avoid a comparison and subtraction or two during lookup.

此策略是否有帮助取决于您的情况下内存使用量有多大。但是为了避免在查找过程中进行一两次比较和减法,存储四倍于您需要的值似乎很浪费。

I second Jeremy's recommendation to measure whether building a table is better than just using std::sin(). Even with the original large table, you'll have to spend cycles during each table lookup to convert the argument to the closest increment of pi/1000, and you'll lose some accuracy in the process.

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

If you're really trying to trade accuracy for speed, you might try approximating the sin() function using just the first few terms of the Taylor series expansion.

如果您真的想用准确性来换取速度,您可以尝试仅使用泰勒级数展开式的前几项来逼近 sin() 函数。

  • sin(x) = x - x^3/3! + x^5/5! ..., where ^ represents raising to a power and ! represents the factorial.
  • sin(x) = x - x^3/3!+ x^5/5!...,其中 ^ 代表升至幂,而 ! 代表阶乘。

Of course, for efficiency, you should precompute the factorials and make use of the lower powers of x to compute higher ones, e.g. use x^3 when computing x^5.

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

One final point, the truncated Taylor series above is more accurate for values closer to zero, so its still worthwhile to map to the first or fourth quadrant before computing the approximate sine.

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

Addendum: Yet one more potential improvement based on two observations:
1. You can compute any trig function if you can compute both the sine and cosine in the first octant [0,pi/4]
2. The Taylor series expansion centered at zero is more accurate near zero

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

So if you decide to use a truncated Taylor series, then you can improve accuracy (or use fewer terms for similar accuracy) by mapping to either the sine or cosine to get the angle in the range [0,pi/4] using identities like sin(x) = cos(pi/2-x) and cos(x) = sin(pi/2-x) in addition to the ones above (for example, if x > pi/4 once you've mapped to the first quadrant.)

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

Or if you decide to use a table lookup for both the sine and cosine, you could get by with two smaller tables that only covered the range [0,pi/4] at the expense of another possible comparison and subtraction on lookup to map to the smaller range. Then you could either use less memory for the tables, or use the same memory but provide finer granularity and accuracy.

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

回答by Mike DeSimone

You'll want the std::sin()function from <cmath>.

你会想要std::sin()函数来自<cmath>.

回答by valdo

One more point: calling trigonometric functions is pricey. if you want to prepare the lookup table for sine with constant step - you may save the calculation time, in expense of some potential precision loss.

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

Consider your minimal step is "a". That is, you need sin(a), sin(2a), sin(3a), ...

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

Then you may do the following trick: First calculate sin(a) and cos(a). Then for every consecutive step use the following trigonometric equalities:

然后你可以做以下的技巧:首先计算 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)
  • 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)

The drawback of this method is that during this procedure the round-off error is accumulated.

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

回答by Svisstack

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

回答by Master Yoda


double table[1000] = {0};
for (int i = 1; i <= 1000; i++)
{
    sine_table[i-1] = std::sin(PI * i/ 1000.0);
}

double getSineValue(int multipleOfPi){ if(multipleOfPi == 0) return 0.0; int sign = 1; if(multipleOfPi < 0){ sign = -1;
} return signsine_table[signmultipleOfPi - 1]; }

You can reduce the array length to 500, by a trick sin(pi/2 +/- angle) = +/- cos(angle). So store sin and cos from 0 to pi/4. I don't remember from top of my head but it increased the speed of my program.

您可以通过一个技巧 sin(pi/2 +/- angle) = +/- cos(angle) 将数组长度减少到 500。所以将 sin 和 cos 从 0 存储到 pi/4。我从头顶上记不清了,但它提高了我的程序速度。

回答by com.prehensible

another approximation from a book or something

书本或其他东西的另一种近似

streamin ramp;
streamout sine;

float x,rect,k,i,j;

x = ramp -0.5;

rect = x * (1 - x < 0 & 2);
k = (rect + 0.42493299) *(rect -0.5) * (rect - 0.92493302) ;
i = 0.436501 + (rect * (rect + 1.05802));
j = 1.21551 + (rect * (rect - 2.0580201));
sine = i*j*k*60.252201*x;

full discussion here: http://synthmaker.co.uk/forum/viewtopic.php?f=4&t=6457&st=0&sk=t&sd=a

完整讨论:http: //synthmaker.co.uk/forum/viewtopic.php?f=4&t=6457&st=0&sk=t&sd=a

I presume that you know, that using a division is a lot slower than multiplying by decimal number, /5 is always slower than *0.2

我想你知道,使用除法比乘以十进制数慢很多,/5 总是比 *0.2 慢

it's just an approximation.

这只是一个近似值。

also:

还:

streamin ramp;
streamin x;  // 1.5 = Saw   3.142 = Sin   4.5 = SawSin
streamout sine;
float saw,saw2;
saw = (ramp * 2 - 1) * x;
saw2 = saw * saw;

sine = -0.166667 + saw2 * (0.00833333 + saw2 * (-0.000198409 + saw2 * (2.7526e-006+saw2 * -2.39e-008)));
sine = saw * (1+ saw2 * sine);