2016-11-15 1 views
0

문제는 :삼각법/부동 소수점 문제

간단한 SDL 2.0.4 응용 프로그램 (하향식 (top-down) 이동/회전 시도)로 math.h의 삼각 함수를 사용하여, 나는 약간의 오류에서이 있었다는 것을 발견 삼각법 계산 결과, '플레이어'가 직면하고있는 방향으로 정확히 움직이지 않아서 나를 많이 괴롭혔습니다. 왜 이것이 될 수 있는지 검색해 보았습니다. 그 주된 이유는 부동 소수점 산술입니다.

나는 libfixmath라는 고정 소수점 수학 라이브러리를 사용했다. 문제는 해결되었지만 어느 정도까지만 해결되었다. 라디안으로 반환 된 90 도의 코사인은 0이 아닌 0.00775146입니다. 그러나 270 도의 코사인은 0 라디안을 반환했습니다! 나는이 문제가 나를 찔 렸고 약간의 도움이 필요하다는 것을 인정해야한다. (나의 수학 기술은별로 도움이되지 않는다.) 고정 소수점 연산에서 사용

변수 :

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

그리고 'INT의 main (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과 sinRadian이 할당되면 일어나는 일은 방향 (라디안에서 각도로 변환 됨)이 고정 소수점 숫자로 변환 된 다음 코사인과 사인을 개별적으로 계산 한 다음 고정 소수점에서 변환 된 것입니다 배정을 위해 숫자를 이중으로 되돌리고, 모두를 libfixmath 라이브러리의 se. 여기

현재 프로그램 (필요한 .dll 파일과 함께이 .exe로 컴파일 나는 정적 libfixmath 라이브러리를 링크) 그래서 당신은 자신에 대한 문제를 볼 수 있습니다 https://mega.nz/#!iJggRbxY!ySbl-2X_oiJKFACyp_kLg9yuLcEsFM07lTRqLtKCsy4

어떤 아이디어 왜 이것으로 일어나고있다 ?

+0

고정 소수점 변환을 별도의 단계로 세분화하고 중간 값을 살펴 봅니다. 정확성이 감소한다는 것을 강력하게 의심합니다. – doug

+2

라디안 변환에서 pi와 같은 근사한 근사값을 사용하는 이유는 무엇입니까? pi에 가장 가까운 double은 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에서 normal sin 및 cos 함수를 사용할 수없는 이유가 있습니까? 이것이 프레임 당 한 번 플레이어에게만 수행된다면 그것은 스피드 치명적인 계산이되어서는 안됩니다.

한 가지 더 : 플레이어 x와 y 위치가 정수 또는 배로 저장되어 있습니까? 정수형이라면 각 프레임의 적분 량을 이동해야하기 때문에 원하는 정확한 방향으로 이동할 수 없습니다. 이를 해결하기 위해 x, y 위치에 2 개의 double 변수를 유지하고 각 프레임에 이동 벡터를 추가 할 수 있습니다. 그런 다음 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 개의 복식 유지에 대한 귀하의 제안은 절대로 발생하지 않았을 것입니다. –