我想通过使用从0到UCHAR_MAX的数组索引来创建用于优化的sin cos查找表,以便0弧度是索引0,pi/2弧度是UCHAR_MAX/4:

辛科斯

#include <limits.h>
#include <math.h>
int sini[UCHAR_MAX];
int cosi[UCHAR_MAX];
#define MAGNIFICATION 256
#define SIN(i) sini[i]/MAGNIFICATION
#define COS(i) cosi[i]/MAGNIFICATION

void initTable(){
    for(int i=0;i<UCHAR_MAX;i++){
        sini[i]=sinf(i*2*M_PI/UCHAR_MAX)*MAGNIFICATION;
        cosi[i]=cosf(i*2*M_PI/UCHAR_MAX)*MAGNIFICATION;
    }
}

使用UCHAR_MAX为max的原因是我想充分利用unsigned char溢出来模拟从0到2*pi的弧度,例如:如果radian的值为2*pi,则数组的索引变为UCHAR_MAX,因为它溢出,它自动变为0,并且不需要mod(如果我使用0到360作为域,则每次可能都需要计算index%360)。然后,我用一些弧度值对其进行测试:
float rad[]={2.0f,4.0f,6.0f,8.0f,10.0f,-2.0f,-4.0f,-6.0f,-8.0f,-10.0f};

如下所示:
#include "sincos.h"
#include <stdio.h>
int main(){
    initTable();
    unsigned char radToIndex;
    float rad[]={2.0f,4.0f,6.0f,8.0f,10.0f,-2.0f,-4.0f,-6.0f,-8.0f,-10.0f};
    int scalar=123;
    printf("scalar=%d\n",scalar);
    for(int i=0;i<sizeof(rad)/sizeof(float);i++){
        radToIndex=rad[i]*UCHAR_MAX/2/M_PI;
        printf("%d*sin(%f) : %f , %d\n",scalar,rad[i],scalar*sinf(rad[i]),scalar*SIN(radToIndex));
    }
    return 0;
}

我用123*sin(radian)测试表,发现当弧度的大小增加时(弧度为10或-10时),结果开始超出实际值:
scalar=123
123*sin(2.000000) : 111.843582 , 111
123*sin(4.000000) : -93.086708 , -92
123*sin(6.000000) : -34.368107 , -35
123*sin(8.000000) : 121.691063 , 122
123*sin(10.000000) : -66.914597 , -61
123*sin(-2.000000) : -111.843582 , -112
123*sin(-4.000000) : 93.086708 , 90
123*sin(-6.000000) : 34.368107 , 38
123*sin(-8.000000) : -121.691063 , -122
123*sin(-10.000000) : 66.914597 , 59

并测试其他数据:
float rad[]={0.01f,0.1f,1.0f,10.0f,100.0f,1000.0f,-0.01f,-0.1f,-1.0f,-10.0f,-100.0f,-1000.0f};

输出:
scalar=123
123*sin(0.010000) : 1.229980 , 0
123*sin(0.100000) : 12.279510 , 12
123*sin(1.000000) : 103.500931 , 102
123*sin(10.000000) : -66.914597 , -61
123*sin(100.000000) : -62.282974 , -97
123*sin(1000.000000) : 101.706184 , -25
123*sin(-0.010000) : -1.229980 , 0
123*sin(-0.100000) : -12.279510 , -8
123*sin(-1.000000) : -103.500931 , -100
123*sin(-10.000000) : 66.914597 , 59
123*sin(-100.000000) : 62.282974 , 98
123*sin(-1000.000000) : -101.706184 , 22

当幅度增加时,误差会增加,因此,我很确定弧度较大时表格会变得不准确。在sincos.h中,有一个值MAGNIFICATION来控制精度,我将其从256更改为4096,但似乎没有太大改进:
scalar=123
123*sin(0.010000) : 1.229980 , 0
123*sin(0.100000) : 12.279510 , 12
123*sin(1.000000) : 103.500931 , 102
123*sin(10.000000) : -66.914597 , -62
123*sin(100.000000) : -62.282974 , -97
123*sin(1000.000000) : 101.706184 , -25
123*sin(-0.010000) : -1.229980 , 0
123*sin(-0.100000) : -12.279510 , -9
123*sin(-1.000000) : -103.500931 , -100
123*sin(-10.000000) : 66.914597 , 59
123*sin(-100.000000) : 62.282974 , 99
123*sin(-1000.000000) : -101.706184 , 22

为什么会这样?表格有逻辑错误吗?

最佳答案

[编辑]

由于OP后续代码中的错误“模数”运算法则,当角度增加超过360度时,代码会遇到问题。乘积rad[i]*UCHAR_MAX/2/M_PI转换为模数为256的(8位)unsigned char,但是代码通过UCHAR_MAX(255)缩放表和代码。该答案的最后一点详细说明了此问题的各个方面,但是很显然,表和代码应使用256,而不是255。

unsigned char radToIndex;
radToIndex=rad[i]*UCHAR_MAX/2/M_PI; // wrong scaling
radToIndex=rad[i]*(UCHAR_MAX+1)/2/M_PI;  // right

此外,请注意,当radToIndex == UCHAR_MAX时,OP的代码具有未定义的行为,因为这是int sini[UCHAR_MAX];的无效索引。

使用上面的修复方法和下面的3种修复方法:表大小256,舍入索引,对正弦值进行舍入,在表创建结果中使用double进行以下操作:
123*sin(2.000000) : 111.843584 , 112
123*sin(4.000000) : -93.086707 , -93
123*sin(6.000000) : -34.368106 , -35
123*sin(8.000000) : 121.691064 , 121
123*sin(10.000000) : -66.914597 , -65
123*sin(-2.000000) : -111.843584 , -112
123*sin(-4.000000) : 93.086707 , 93
123*sin(-6.000000) : 34.368106 , 35
123*sin(-8.000000) : -121.691064 , -121
123*sin(-10.000000) : 66.914597 , 65

代码也遇到了double rounding或更宝贵的东西:双截断。
radToIndex=rad[i]*UCHAR_MAX/2/M_PI;截断为0。因此,使索引变小,而不是最接近。

表创建sini[i]=sinf(i*2*M_PI/UCHAR_MAX)*MAGNIFICATION;也会向0截断。因此sini[]变小了,而不是最接近的int

要改进,只需使用round()舍入到最接近的值。
sini[i] = (int) roundf(sinf(i*2*M_PI/UCHAR_MAX)*MAGNIFICATION);
radToIndex= (int) round(rad[i]*UCHAR_MAX/2/M_PI);

作为一般说明,由于float通常为24位精度,而int可能为31+符号,因此可以使用double创建表以进行其他改进。
sini[i] = (int) round(sin(i*2.0*M_PI/UCHAR_MAX)*MAGNIFICATION);

此外,建议使用UCHAR_MAX + 1参见BAM:

减1。


UCHAR_MAX不溢出,UCHAR_MAX + 1溢出并变为0。(unsigned char数学)
int sini[UCHAR_MAX+1];
for (int i=0; i<(UCHAR_MAX+1); i++) {
  // Rather than `i*2*M_PI/UCHAR_MAX`, use
  sini[i]=sinf(i*2*M_PI/(UCHAR_MAX + 1))*MAGNIFICATION;

09-25 20:57