0
\$\begingroup\$

I want to check if the Y position is changing or not for the player and the enemy. If it has changed in the frame I want it to return true and if it didn't I want it to return false. I round because the enemy's position alternates between 0.8 and 0.800001 .

I tried to do Mathf.Round(transform.position * 100)/100 and I compare is to the enemyOldPosition which I assigned using enemyOldPosition = Mathf.Round(transform.position * 100)/100, but it says it's not equal.

I tried to check Debug.Log(enemyOldPosition - Mathf.Round(transform.position * 100)/100) and it returned 1.192093E-08.

void Update()
{
     if(Mathf.Round(player.transform.position.y*100)/100 == oldPosition)
     {
         isChangingY = false;
         oldPosition = Mathf.Round(player.transform.position.y*100)/100;
     }
     else
     {
         Debug.Log(oldPosition - Mathf.Round(player.transform.position.y*100)/100);
         isChangingY = true;
         oldPosition = Mathf.Round(player.transform.position.y*100)/100;
     }


     if(Mathf.Round(transform.position.y*100)/100 == enemyOldPosition)
     {
         enemyIsChangingY = false;
         enemyOldPosition = Mathf.Round(transform.position.y*100)/100;
     }
     else
     {
         Debug.Log(enemyOldPosition - Mathf.Round(transform.position.y*100)/100);
         enemyIsChangingY = true;
         enemyOldPosition = Mathf.Round(transform.position.y*100)/100;
     }

 }

This script is placed my on enemy, and they both have rigidbodies

\$\endgroup\$
  • 1
    \$\begingroup\$ If in future you have a case where it looks like some standard utility function like Round is giving the wrong output, I recommend breaking your calculation into separate steps, so you can examine the value at each step. It's very rarely the math API that's the culprit. \$\endgroup\$ – DMGregory Apr 9 at 18:07
1
\$\begingroup\$

You generally want to avoid comparing floats for exact equality. Instead, use a range.

float closeEnough = 1f/100f;

float differenceFromPrevious = transform.position.y - oldPosition.y;

if(Mathf.Abs(differenceFromPrevious) <= closeEnough) {
   // ...do your stationary stuff, but leave oldPosition where it was to avoid drift.
} else {
   // ...do your moving stuff, and update oldPosition to reflect the movement.
   oldPosition = transform.position;
}

If you want context as to why your subtraction isn't giving you an answer of zero as you expect, it's not that the round function is returning the wrong value. It's that you're not comparing apples to apples.

It's important to know that the intermediate registers your CPU uses to evaluate float expressions are actually wider than 32 bits: that way you minimize rounding errors that can accumulate in intermediate steps in a calculation. But values you save to RAM get rounded to 32-bit precision.

So let's say your y position is actually 1.01, or as close to it as we can represent in a 32-bit float, which is \$1.00999999_{10}\$ in decimal, or \$1.000 \, 000 \, 101 \, 000 \, 111 \, 101 \, 011 \, 10_2\$ (\$1 + 83886 * 2^{-23}\$) in binary. The digit expansion loops forever, since it's not a sum of powers of 2 (dividing by 100 means there's a factor of 5 in the denominator) but we only get to keep 24 digits of it, counting the leading 1.

We multiply it by 100, round it to 101, then divide by 100. We can't represent the output of the division exactly, but in our register we can keep about 53 binary digits, or \$1.000 \, 000 \, 101 \, 000 \, 111 \, 101 \, 011 \, 100 \, 001 \, 010 \, 001 \, 111 \, 010 \, 111 \, 000 \, 010 \, 100 \, 1_2\$ (really really close!) which we store in our oldPosition variable.

Then you save that value to a float, which rounds it to 32-bit precision, 1.00999999.

Next frame we read that variable as the rounded version we stored: 1.00999999, but your newly-calculated transform.position.y*100)/100 is part of an expression that hasn't been fully evaluated yet (we still have the subtraction to do), so it's still at register precision! So when we do the subtraction we get:

$$\begin{align} &1.000 \, 000 \, 101 \, 000 \, 111 \, 101 \, 011 \, 10_2\\ - &1.000 \, 000 \, 101 \, 000 \, 111 \, 101 \, 011 \, 100 \, 001 \, 010 \, 001 \, 111 \, 010 \, 111 \, 000 \, 010 \, 100 \, 1_2\\ = \, -&0.000 \, 000 \, 000 \, 000 \, 000 \, 000 \, 000 \, 000 \, 001 \, 010 \, 001 \, 111 \, 010 \, 111 \, 000 \, 010 \, 100 \, 1_2 \end{align}$$

Which ends up being about \$0.000 \, 000 \, 009 \, 536 \, 743 \, 172 \, 944 \, 284 \, 2_{10}\$ away from zero in decimal, or 1.192093E-08 as reported by your debug log. Definitely not equal to zero!

So your program is telling you the truth. You just asked it a slightly different question than you meant to.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You're teaching me so much, thanks dude :) \$\endgroup\$ – Samuel Fyckes Apr 9 at 17:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.