3

So I'm having what seems to me to be a very bizarre problem. I've got a crude system for applying forces to objects on 2D planes, and one of the simplest calculations seems to be causing one of my variables to overflow. I have the following line:

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

Where Momentum has two data members, x y (m_Momentum is an SFML sf::Vector2 of floats). Now, normally the formula should always return either 1 or -1, depending on the sign of Momentum.y (unless I'm grossly mistaken).

However, it occasionally returns insanely high numbers such as -2147483648. In that particular case, the value of m_Momentum.y was 0.712165 (both values were obtained by sending to std::cout); I tried again, m_Momentum.y was -0.578988 and ySign was still -2147483648. There is a corresponding xSign that also flips out sometimes, often with the same final value. I can't confirm 100% that this is always the result, but at the moment that seems to be the case.

I'm sort of stumped as to why this is happening, and when it does, it basically invalidates my program (it instantly sends objects millions of pixels in the wrong direction). It seems logically impossible that the line above is returning such strange results.

Below is the function I am working on. Probably the wrong way to do it, but I didn't expect it to go so horribly wrong. The printout it produces reveals that all numbers look normal until the signs are printed out; one of them is invariably massive, and afterwards you see numbers like -2.727e+008 (which, as far as I'm aware, is scientific notation - i.e. -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);

Can somebody explain what's going on here?

EDIT: RedX helpfully directed me to the following post: Is there a standard sign function (signum, sgn) in C/C++? Which led me to write the following lines of code:

//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);

Thanks to the above, I no longer have the strange problem. For an explanation/alternative solution, see Didier's post below.

Community
  • 1
  • 1
GarrickW
  • 2,181
  • 5
  • 31
  • 38
  • 3
    If its just the multiplication you should be able to produce a self-contained testcase in under 10 lines. – PlasmaHH Feb 01 '12 at 11:19
  • 1
    it looks like you are trying to get the signum (or direction (+1/-1) in this case out of the vector component? If yes see this thread http://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c You should also read these: https://encrypted.google.com/url?sa=t&rct=j&q=what%20every%20programmer%20should%20know%20about%20floating%20point&source=web&cd=1&ved=0CCUQFjAA&url=http%3A%2F%2Fdocs.oracle.com%2Fcd%2FE19957-01%2F806-3568%2Fncg_goldberg.html&ei=-x8pT8X8I87s-gbPvMCwBQ&usg=AFQjCNHSL-lclWJrAUgNWVbQ6BAvZRoP9Q&cad=rja http://floating-point-gui.de/ – RedX Feb 01 '12 at 11:19
  • @ RedX Thank you very much! I used the top response's code and I no longer have the problem. I will add the better option to the bottom of my post, for future reference. Still, I'd be curious to know exactly why my code was acting strange. Might be useful knowledge in the future. – GarrickW Feb 01 '12 at 11:26
  • consider using `copysignf(1.0, value)`, from: [standard sign function](http://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c) – Andreas Feb 17 '14 at 20:08

2 Answers2

8

You should use fabs() instead of abs() to get the absolute value of a floating point number. If you use the integer absolute function, then the result is an integer ...

For instance, -0.5 / abs(-0.5) is treated as -0.5 / 0 which results in negative infinity (as a floating point value) that is converted to the minimum value of an int 0x80000000 = -2147483648

Didier Trosset
  • 36,376
  • 13
  • 83
  • 122
  • 4
    Or `std::abs`, since this is C++. – Mike Seymour Feb 01 '12 at 11:27
  • Ahh, that would explain the issue. Thank you for the explanation! I'll keep this in mind in the future. – GarrickW Feb 01 '12 at 11:28
  • 2
    This makes a really good example for the practice of enabling warnings and paying attention to them. For me, I have found that using the most strict warning level, and treating warnings as compile errors, saves much more time than it costs. – Mark Taylor Feb 03 '12 at 03:01
0

Taking absolute values and dividing sounds like an awful waste of cycles to me. What's wrong with

x > 0 ? 1 : -1

which you could always put in a function

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