2012-02-01 2 views
3

Итак, у меня есть то, что кажется мне очень странной проблемой. У меня есть грубая система для применения сил к объектам на 2D-плоскостях, и один из самых простых вычислений, по-видимому, вызывает переполнение одной из моих переменных. У меня есть следующие строки:Разделение поплавка самопроизвольно производит очень большие целые числа

int ySign = m_Momentum.y/abs(m_Momentum.y);

Где Momentum имеет два элемента данных, xy (m_Momentum является SFML научной фантастики :: Vector2 поплавков). Теперь, как правило, формула должна всегда возвращать либо 1, либо -1, в зависимости от знака Momentum.y (если я не ошибаюсь).

Однако он иногда возвращает безумно высокие цифры, такие как -2147483648. В этом конкретном случае значение m_Momentum.y было 0,712165 (оба значения были получены путем отправки в std :: cout); Я попробовал еще раз, m_Momentum.y был -0.578988, а ySign остался -2147483648. Существует соответствующий xSign, который также иногда выворачивается, часто с тем же конечным значением. Я не могу подтвердить 100%, что это всегда результат, но на данный момент это похоже.

Я как бы в тупике, почему это происходит, и когда это происходит, это в основном делает недействительной мою программу (она мгновенно посылает объекты миллионам пикселей в неправильном направлении). Кажется логически невозможным, чтобы линия выше возвращала такие странные результаты.

Ниже приведена функция, над которой я работаю. Возможно, это неправильный способ сделать это, но я не ожидал, что это будет так ужасно неправильно. Распечатка, которую он производит, показывает, что все номера выглядят нормально до тех пор, пока знаки не будут распечатаны; один из них неизменно массивный, а затем вы видите числа, такие как -2.727e + 008 (что, насколько мне известно, представляет собой научную нотацию, т. е. -2.727 * 10^8).

///MODIFY MOMENTUM 
//Reset, if necessary 
if (Reset == true) 
{ 
    m_Momentum.x = 0; 
    m_Momentum.y = 0; 
} 
sf::Vector2<float> OldMoment = m_Momentum; 

//Apply the force to the new momentum. 
m_Momentum.x += Force.x; 
m_Momentum.y += Force.y; 
sf::Vector2<float> NewMoment = m_Momentum; 

//Calculate total momentum. 
float sqMomentum = m_Momentum.x * m_Momentum.x + m_Momentum.y * m_Momentum.y; 
float tMomentum = sqrt(sqMomentum); 

//Preserve signs for later use. 
int xSign = m_Momentum.x/abs(m_Momentum.x); 
int ySign = m_Momentum.y/abs(m_Momentum.y); 

//Determine more or less the ratio of importance between x and y components 
float xProp; 
float yProp; 
if (abs(tMomentum) > m_MaxVelocity) 
{ 
    //Get square of maximum velocity 
    int sqMax = m_MaxVelocity * m_MaxVelocity; 
    //Get proportion of contribution of each direction to velocity 
    xProp = (m_Momentum.x * m_Momentum.x)/sqMomentum; 
    yProp = (m_Momentum.y * m_Momentum.y)/sqMomentum; 
    //Reset such that the total does not exceed maximum velocity. 
    m_Momentum.x = sqrt(sqMax * xProp) * xSign; 
    m_Momentum.y = sqrt(sqMax * yProp) * ySign; 
} 

///SANITY CHECK 
//Preserve old tMomentum 
float tOld = tMomentum; 

//Calculate current tMomentum 
sqMomentum = m_Momentum.x * m_Momentum.x + m_Momentum.y * m_Momentum.y; 
tMomentum = sqrt(sqMomentum); 

//If it's still too high, print a report. 
if (tMomentum > m_MaxVelocity) 
{ 
    std::cout << "\n\nSANITY CHECK FAILED\n"; 
    std::cout << "-\n"; 
    std::cout << "Old Components: " << OldMoment.x << ", " << OldMoment.y << "\n"; 
    std::cout << "Force Components: " << Force.x << ", " << Force.y << "\n"; 
    std::cout << "-\n"; 
    std::cout << "New Components: " << NewMoment.x << ", " << NewMoment.y << "\n"; 
    std::cout << "Which lead to...\n"; 
    std::cout << "tMomentum: " << tOld << "\n"; 
    std::cout << "-\n"; 
    std::cout << "Found these proportions: " << xProp << ", " << yProp << "\n"; 
    std::cout << "Using these signs: " << xSign << ", " << ySign << "\n"; 
    std::cout << "New Components: " << m_Momentum.x << ", " << m_Momentum.y << "\n"; 
    std::cout << "-\n"; 
    std::cout << "Current Pos: " << m_RealPosition.x << ", " << m_RealPosition.y << "\n"; 
    std::cout << "New Pos: " << m_RealPosition.x + m_Momentum.x << ", " << m_RealPosition.y + m_Momentum.y << "\n"; 
    std::cout << "\n\n"; 
} 

///APPLY FORCE 
//To the object's position. 
m_RealPosition.x += m_Momentum.x; 
m_RealPosition.y += m_Momentum.y; 

//To the sprite's position. 
m_Sprite.Move(m_Momentum.x, m_Momentum.y); 

Может кто-нибудь объяснить, что здесь происходит?

EDIT: RedX услужливо направил меня к следующему сообщению: Is there a standard sign function (signum, sgn) in C/C++? Который привел меня, чтобы написать следующие строки кода:

//Preserve signs for later use. 
//int xSign = m_Momentum.x/abs(m_Momentum.x); 
//int ySign = m_Momentum.y/abs(m_Momentum.y); 
int xSign = (m_Momentum.x > 0) - (m_Momentum.x < 0); 
int ySign = (m_Momentum.y > 0) - (m_Momentum.y < 0); 

Благодаря вышесказанному, я больше не имеют странную проблему. Для объяснения/альтернативного решения см. Сообщение Дидье ниже.

+3

Если его просто умножение вы должны быть в состоянии производить автономный TestCase в возрасте до 10 линий. – PlasmaHH

+1

похоже, что вы пытаетесь получить signum (или направление (+ 1/-1) в этом случае из векторного компонента? Если да, посмотрите этот поток http: // stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-cc Вы также должны прочитать: https://encrypted.google.com/url?sa=t&rct=j&q=what % 20every% 20programmer% 20should% 20know% 20about% 20floating% 20point & источник = & веб кд = 1 & вед = 0CCUQFjAA & URL = HTTP% 3A% 2F% 2Fdocs.oracle.com% 2Fcd% 2FE19957-01% 2F806-3568% 2Fncg_goldberg.html & е = -x8pT8X8I87s- gbPvMCwBQ & usg = AFQjCNHSL-lclWJrAUgNWVbQ6BAvZRoP9Q & cad = rja http://floating-point-gui.de/ – RedX

+0

@ RedX Большое спасибо! Я использовал код верхнего ответа, и у меня больше нет проблемы. Я добавлю лучший вариант в конец моего сообщения, для дальнейшего использования. Тем не менее, мне было бы интересно узнать, почему мой код действует странно. Может быть полезным знанием в будущем. – GarrickW

ответ

8

Чтобы получить абсолютное значение числа с плавающей запятой, используйте fabs() вместо abs(). Если вы используете целую абсолютную функцию, то результат является целым числом ...

Например, -0.5/abs(-0.5) трактуются как -0.5/0, что приводит к отрицательной бесконечности (как значение с плавающей точкой), которая преобразуется в минимальное значение int 0x80000000 = -2147483648

+4

Или 'std :: abs', так как это C++. –

+0

Ах, это объяснит проблему. Спасибо вам за разъяснение! Я буду помнить об этом в будущем. – GarrickW

+2

Это действительно хороший пример для практики включения предупреждений и обращения к ним. Для меня я обнаружил, что использование самого строгого уровня предупреждения и обработка предупреждений как ошибок компиляции экономит гораздо больше времени, чем это стоит. –

0

Принимая абсолютные значения и делящие звуки, как ужасная трата циклов для меня. Что случилось с

x > 0 ? 1 : -1 

, который вы всегда можете поставить в функции

template <class T> 
inline int sgn(const T &x) { return x > 0 ? : 1; }