Game Development Community

Rounding error in Move::clamp()

by Tom Spilman · in Torque Game Engine · 08/25/2006 (12:13 am) · 2 replies

I came across this one when i started filtering my joystick input values using a large exponent.

Move::clamp(), in gameConnectionMoves.cc, converts the x,y,z moves into an integer in the range of 0 to 32, puts them in px, py, pz, and then converts it back to an F32 in the -1 to 1 range. I guess it does this for network transmission.... but whatever the case the user input is subtly adjusted.

The problem...

curMove.x = -0.0624;
curMove.clamp();
assert( curMove.x == -0.0625 ); // this is correct....

curMove.x = 0.0624;
curMove.clamp();
assert( curMove.x == 0.0625 ); // this fails... it equals 0

So for positive values below 0.0625 clamp() always returns 0... which isn't true in the negative case. So negative input is more accurate than positive input values.

The true culprit here is clampRangeClamp() which Move::clamp() uses to convert the F32 into an S32....

static inline S32 clampRangeClamp(F32 val)
{
   if(val < -1)
      return 0;
   if(val > 1)
      return 32;
   return (S32)((val + 1.03125) * 16); // Fix: 0.5 / 16 = 0.03125 ... this forces a round up.
}

I've tested this pretty thoroughly, but would love some more eyeballs on it.

About the author

Tom is a programmer and co-owner of Sickhead Games, LLC.


#1
05/14/2007 (10:49 am)
Old thread... but still a subtle bug in TGE.
#2
09/11/2007 (3:52 pm)
I glanced at this and was curious why the original code sent 32 at the +1 boundary. It seems that a variable ranged 0..32 is incompatible with the idea of saving every bit in this moves application where IIRC the data is sent every packet.

Seems like there is a possibility to both save 3 bits per packet (woohoo! :) ) and to improve rounding near zero and +1 (as Tom's mod does). The bit savings comes from

coding the -1..+1 range into 0..30 in 5 bits (wasting one bin) rather than 0..32 in 6 bits wasting 31 bins.

Alternately, one could fill up the 6 bits by coding -1 .. +1 into 0..62. Again wasting 1 bin to gain rounding accuracy around zero and +1, but wasting 30 less bins than the current code and roughly halving the maximum rounding errors.

One downside of any change would seem to be making move streams saved with previous versions different slightly (0..30 in 5 bits) or incompatible (0..62 in 6 bits).

----
OK, back to initializing databl**ks now ;)