如何用 C++ 重写以下伪代码?
real array sine_table[-1000..1000]
for x from -1000 to 1000
sine_table[x] := sine(pi * x / 1000)
我需要创建一个 sine_table 查找表。
如何用 C++ 重写以下伪代码?
real array sine_table[-1000..1000]
for x from -1000 to 1000
sine_table[x] := sine(pi * x / 1000)
我需要创建一个 sine_table 查找表。
您可以通过仅存储第一象限的值(即 [0,pi/2] 中的 x)将表的大小减少到原始大小的 25%。
为此,您的查找例程只需使用简单的三角标识将 x 的所有值映射到第一象限:
要从象限 III 映射到 I,应用两个恒等式,即 sin(x) = - sin (pi + x)
此策略是否有帮助取决于您的情况有多少内存使用量。但是,为了避免在查找过程中进行一两次比较和减法,存储四倍于所需的值似乎很浪费。
我支持 Jeremy 的建议,即衡量构建表是否比仅使用 std::sin() 更好。即使使用原始的大表,您也必须在每次查找表期间花费循环来将参数转换为最接近的 pi/1000 增量,并且您将在此过程中失去一些准确性。
如果您真的想以准确性换取速度,您可以尝试仅使用泰勒级数展开的前几项来逼近 sin() 函数。
当然,为了提高效率,您应该预先计算阶乘并利用 x 的低次方来计算高次方,例如在计算 x^5 时使用 x^3。
最后一点,上面截断的泰勒级数对于接近零的值更准确,因此在计算近似正弦之前映射到第一或第四象限仍然值得。
附录:基于两个观察结果的另一个潜在改进:
1. 如果您可以计算第一个八分圆 [0,pi/4] 中的正弦和余弦,则可以计算任何三角函数
2. 以零为中心的泰勒级数展开是在零附近更准确
因此,如果您决定使用截断的泰勒级数,那么您可以通过映射到正弦或余弦来提高准确度(或使用更少的项来获得相似的准确度),以使用诸如sin(x) = cos(pi/2-x) 和 cos(x) = sin(pi/2-x) 除了上面的那些(例如,如果 x > pi/4 一旦你映射到第一象限。)
或者,如果您决定对正弦和余弦都使用表查找,则可以使用仅覆盖范围 [0,pi/4] 的两个较小的表,而代价是查找映射到的另一个可能的比较和减法范围越小。然后,您可以为表使用更少的内存,或者使用相同的内存但提供更精细的粒度和准确性。
long double sine_table[2001];
for (int index = 0; index < 2001; index++)
{
sine_table[index] = std::sin(PI * (index - 1000) / 1000.0);
}
还有一点:调用三角函数很昂贵。如果您想为具有恒定步长的正弦准备查找表 - 您可以节省计算时间,但会牺牲一些潜在的精度损失。
考虑您的最小步骤是“a”。也就是说,你需要 sin(a), sin(2a), sin(3a), ...
然后你可以做以下技巧:首先计算 sin(a) 和 cos(a)。然后对于每个连续的步骤,使用以下三角等式:
这种方法的缺点是在此过程中会累积舍入误差。
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];
}
您可以通过技巧 sin(pi/2 +/- angle) = +/- cos(angle) 将数组长度减少到 500。所以将 sin 和 cos 从 0 存储到 pi/4。我不记得了,但它提高了我的程序的速度。
您将需要std::sin()
来自 的功能<cmath>
。
书或其他东西的另一个近似值
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;
完整的讨论在这里: http ://synthmaker.co.uk/forum/viewtopic.php?f=4&t=6457&st=0&sk=t&sd=a
我想你知道,使用除法比乘以十进制数要慢得多,/5 总是比 *0.2 慢
这只是一个近似值。
还:
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);