2016-11-15 51 views
0

问题:三角/浮点问题

当使用math.h中的三角函数用一个简单的SDL 2.0.4应用程序(自顶向下移动/旋转未遂),我发现有一些轻微的错误三角函数的计算,导致'玩家'不能完全按照所面临的方向移动,这给我带来了很多困扰。我搜索了为什么会出现这种情况,主要原因似乎是浮点运算。

我使用了一个叫做libfixmath的定点数学库 - 并且问题解决了,但只是在某种程度上。以弧度返回的90度余弦为0.00775146,而不是0;然而,270度的余弦确实返回了0弧度!我必须承认这个问题让我陷入困境,我需要一些帮助(我的数学技能不是很好,这没有帮助)。

在定点算术中使用的变量:

double direction = 0.0; 
double sinRadians = 0.0; 
double cosRadians = 1.0; // presuming player starts facing direction of 0 degrees! 

然后 '诠释主(INT的argc,字符* argv的[])' 涉及这些变量的部分:

if (keyDownW == true) 
    { 
     Player.setXPos(Player.getXPos() + (10 * sinRadians)); 
     Player.setYPos(Player.getYPos() - (10 * cosRadians)); // +- = - 
     cout << "Cosine and Sine (in radians): " << cosRadians << ", " << sinRadians << endl; 
     cout << direction << " degrees \n" << endl; 
    } 
    else if (keyDownS == true) 
    { 
     Player.setXPos(Player.getXPos() - (6 * sinRadians)); 
     Player.setYPos(Player.getYPos() + (6 * cosRadians)); // -- = + 
    } 
    if (keyDownA == true) 
    { 
     if (direction <= 0) 
     { 
      direction = 345; 
     } 
     else 
     { 
      direction -= 15; 
     } 
     direction = direction * (3.14159/180); // convert to radians 
     cosRadians = fix16_to_dbl(fix16_cos(fix16_from_dbl(direction))); 
     sinRadians = fix16_to_dbl(fix16_sin(fix16_from_dbl(direction))); 
     direction = direction * (180/3.14159); // convert back to degrees 
    } 
    else if (keyDownD == true) 
    { 
     if (direction >= 345) 
     { 
      direction = 0; 
     } 
     else 
     { 
      direction += 15; 
     } 
     direction = direction * (3.14159/180); // convert to radians 
     cosRadians = fix16_to_dbl(fix16_cos(fix16_from_dbl(direction))); 
     sinRadians = fix16_to_dbl(fix16_sin(fix16_from_dbl(direction))); 
     direction = direction * (180/3.14159); // convert back to degrees 
    } 

当cosRadians和sinRadians被分配时,发生的是方向(已经从度数转换为弧度)被转换为一个定点数,然后用它来分别计算余弦和正弦,然后从一个定点数字回到分配的双重位置,所有与你se libfixmath库。

这里是目前的程序(编译为必要的.dll文件的.exe;我静态链接libfixmath库)所以你可以看到这个问题自己:https://mega.nz/#!iJggRbxY!ySbl-2X_oiJKFACyp_kLg9yuLcEsFM07lTRqLtKCsy4

任何想法,为什么发生这种情况?

+0

将定点转换分解为单独的步骤并查看中间值。强烈怀疑这就是你会发现精度的降低。 – doug

+2

为什么在弧度转换中这样粗略地逼近pi?与pi最接近的两倍是3.141592653589793 –

+0

如果你只能移动NSEW,为什么你要使用float? – stark

回答

1

一个问题是,您正在从度数来回转换为弧度并返回每一帧,并且每次都会失去精度。相反,请转换为临时变量,如下所示:

double direction_radians = direction * (3.14159/180); // convert to radians 
    cosRadians = fix16_to_dbl(fix16_cos(fix16_from_dbl(direction_radians))); 
    sinRadians = fix16_to_dbl(fix16_sin(fix16_from_dbl(direction_radians))); 

这样错误不会累积。

另外,是否有任何理由,你不能只使用math.h中的正常sin和cos函数而不是定点版本?如果这仅仅是每帧一次对玩家进行的,它不应该是速度临界计算。

还有一件事:播放器的x和y位置是以整数还是双精度来存储的?如果它是一个整数,那么你将无法沿着你想要的精确方向移动,因为你必须移动每帧的整数量。为了解决这个问题,您可以为x,y位置保留2个双变量,并将每个帧的运动矢量添加到它们。然后通过转换为int来设置x和y。这会阻止你每次都失去你职位的小数部分。

例如

playerX += (10 * sinRadians); 
playerY -= (10 * cosRadians); 
Player.setXPos((int)playerX); 
Player.setYPos((int)playerY); 
+0

谢谢,我没有意识到错误积累!我选择了定点版本,主要是针对浮点的小算术问题,看看它是否有助于解决问题;看起来确实有一点,但显然这个问题并没有完全消失。再次感谢您的答案!我明天将实施您的解决方案,因为目前我已经很晚了。我会回来接受你的答案然后:) –

+0

@ T.Lane你将不得不接受一些舍入误差,因为像pi这样的数字根本没有确切的定点值。诀窍是限制错误的积累,特别是在同一方向上的错误。 –

+0

是的,玩家的x和y都是整数值。你对维持2次双打的建议从来没有发生过,非常感谢! –