我想通过使用从0到UCHAR_MAX
的数组索引创建一个sin cos查找表进行优化,因此0弧度为索引0,pi/2
弧度为UCHAR_MAX/4
:
sincos.h
#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
作为最大值的原因是我想充分利用无符号字符串溢出来模拟仅从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
为什么会发生这种情况?表有逻辑错误吗?
答案 0 :(得分:5)
[编辑]
由于错误&#34; modulo&#34;角度增加超过360度,代码会遇到问题。 OP代码中的算术运算。产品rad[i]*UCHAR_MAX/2/M_PI
被转换为(8位)unsigned char
,其模数为256,但代码是按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
此外,注意OP的代码在radToIndex == UCHAR_MAX
时具有未定义的行为,因为这是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,因为它溢出,它会自动变为0
UCHAR_MAX
没有溢出,UCHAR_MAX + 1
溢出并变为0.(unsigned char
math)
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;
答案 1 :(得分:0)
问题来源
看起来您从浮点数的舍入和将浮点数分配给unsigned char
时会出现错误。
以下程序根据您发布的代码进行了改编,演示了即使在对浮点数进行舍入后索引也会开始偏离。
#include <limits.h>
#include <math.h>
int sini[UCHAR_MAX];
int cosi[UCHAR_MAX];
double angle[UCHAR_MAX];
#define MAGNIFICATION 256
#define SIN(i) sini[i]/MAGNIFICATION
#define COS(i) cosi[i]/MAGNIFICATION
void initTable()
{
double M_PI = 4.0*atan(1.0);
for(int i=0;i<UCHAR_MAX;i++)
{
angle[i] = i*2*M_PI/UCHAR_MAX;
sini[i]=sinf(angle[i])*MAGNIFICATION;
cosi[i]=cosf(angle[i])*MAGNIFICATION;
}
}
#include <stdio.h>
void test3()
{
int radToIndexInt;
unsigned char radToIndexChar;
float radTemp;
float rad[]={2.0f,4.0f,6.0f,8.0f,10.0f,-2.0f,-4.0f,-6.0f,-8.0f,-10.0f};
double M_PI = 4.0*atan(1.0);
for(int i=0;i<sizeof(rad)/sizeof(float);i++)
{
radTemp = rad[i]*UCHAR_MAX/2/M_PI;
radToIndexInt = round(radTemp);
radToIndexInt %= UCHAR_MAX;
if ( radToIndexInt < 0 )
{
radToIndexInt += UCHAR_MAX;
}
radToIndexChar = round(radTemp);
printf("radToIndexInt: %d, radToIndexChar: %d\n",
radToIndexInt, radToIndexChar);
}
}
int main()
{
initTable();
test3();
return 0;
}
上述计划的输出:
radToIndexInt: 81, radToIndexChar: 81
radToIndexInt: 162, radToIndexChar: 162
radToIndexInt: 244, radToIndexChar: 244
radToIndexInt: 70, radToIndexChar: 69
radToIndexInt: 151, radToIndexChar: 150
radToIndexInt: 174, radToIndexChar: 175
radToIndexInt: 93, radToIndexChar: 94
radToIndexInt: 11, radToIndexChar: 12
radToIndexInt: 185, radToIndexChar: 187
radToIndexInt: 104, radToIndexChar: 106
<强>解决方案强>
使用
radToIndex=round(radTemp);
radToIndex %= UCHAR_MAX;
if ( radToIndex < 0 )
{
radToIndex += UCHAR_MAX;
}
计算索引,我得到非常接近的答案:
这是一个程序,再次根据您发布的代码进行调整,演示使用上述逻辑。
#include <limits.h>
#include <math.h>
int sini[UCHAR_MAX];
int cosi[UCHAR_MAX];
double angle[UCHAR_MAX];
#define MAGNIFICATION 256
#define SIN(i) sini[i]/MAGNIFICATION
#define COS(i) cosi[i]/MAGNIFICATION
void initTable()
{
double M_PI = 4.0*atan(1.0);
for(int i=0;i<UCHAR_MAX;i++)
{
angle[i] = i*2*M_PI/UCHAR_MAX;
sini[i]=sinf(angle[i])*MAGNIFICATION;
cosi[i]=cosf(angle[i])*MAGNIFICATION;
}
}
#include <stdio.h>
void test2()
{
int radToIndex;
float radTemp;
int scalar=123;
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};
double M_PI = 4.0*atan(1.0);
printf("scalar=%d\n",scalar);
for(int i=0;i<sizeof(rad)/sizeof(float);i++)
{
radTemp = rad[i]*UCHAR_MAX/2/M_PI;
radToIndex=round(radTemp);
radToIndex %= UCHAR_MAX;
if ( radToIndex < 0 )
{
radToIndex += UCHAR_MAX;
}
printf("%d*sin(%f) : %f , %d\n",
scalar,rad[i],scalar*sinf(rad[i]),scalar*SIN(radToIndex));
}
}
void test1()
{
int radToIndex;
float radTemp;
int scalar=123;
float rad[]={2.0f,4.0f,6.0f,8.0f,10.0f,-2.0f,-4.0f,-6.0f,-8.0f,-10.0f};
double M_PI = 4.0*atan(1.0);
printf("scalar=%d\n",scalar);
for(int i=0;i<sizeof(rad)/sizeof(float);i++)
{
radTemp = rad[i]*UCHAR_MAX/2/M_PI;
radToIndex=round(radTemp);
radToIndex %= UCHAR_MAX;
if ( radToIndex < 0 )
{
radToIndex += UCHAR_MAX;
}
printf("%d*sin(%f) : %f , %d\n",
scalar,rad[i],scalar*sinf(rad[i]),scalar*SIN(radToIndex));
}
}
int main()
{
initTable();
test1();
test2();
return 0;
}
输出:
scalar=123
123*sin(2.000000) : 111.843582 , 111
123*sin(4.000000) : -93.086708 , -92
123*sin(6.000000) : -34.368107 , -32
123*sin(8.000000) : 121.691063 , 121
123*sin(10.000000) : -66.914597 , -67
123*sin(-2.000000) : -111.843582 , -111
123*sin(-4.000000) : 93.086708 , 92
123*sin(-6.000000) : 34.368107 , 32
123*sin(-8.000000) : -121.691063 , -121
123*sin(-10.000000) : 66.914597 , 67
scalar=123
123*sin(0.010000) : 1.229980 , 0
123*sin(0.100000) : 12.279510 , 12
123*sin(1.000000) : 103.500931 , 103
123*sin(10.000000) : -66.914597 , -67
123*sin(100.000000) : -62.282974 , -63
123*sin(1000.000000) : 101.706184 , 102
123*sin(-0.010000) : -1.229980 , 0
123*sin(-0.100000) : -12.279510 , -12
123*sin(-1.000000) : -103.500931 , -103
123*sin(-10.000000) : 66.914597 , 67
123*sin(-100.000000) : 62.282974 , 63
123*sin(-1000.000000) : -101.706184 , -102