0

I am making keyboard controls for a camera, and I am experiencing issues regarding the innacuracy of floats. I have the variables "float Xvelocity" and "float Zvelocity", I increment these velocities by adding or subtracting the acceleration speed every time the respective direction keys are being held (A, D, W or S). However, everything I try to make the float go towards zero simply work in very strange ways. I thought it would be as simple as:

if (keys[GLFW_KEY_A] & xVelocity >-speed)
    xVelocity -= acceleration / deltaTime;
if (keys[GLFW_KEY_D] & xVelocity < speed)
    xVelocity += acceleration / deltaTime;
if (keys[GLFW_KEY_W] & zVelocity >-speed)
    zVelocity -= acceleration / deltaTime;
if (keys[GLFW_KEY_S] & zVelocity < speed)
    zVelocity += acceleration / deltaTime;

if (!keys[GLFW_KEY_A] & xVelocity<0.f) {
    xVelocity+=deceleration / deltaTime;
}
if (!keys[GLFW_KEY_D] & xVelocity>0.f) {
    xVelocity-=deceleration / deltaTime;
}
if (!keys[GLFW_KEY_W] & zVelocity<0.f) {
    zVelocity+=deceleration / deltaTime;
}
if (!keys[GLFW_KEY_S] & zVelocity>0.f) {
    zVelocity-=deceleration / deltaTime;
}

origin -= right * xVelocity;
origin -= vec3(front.x, 0.f, front.z) * zVelocity;

This doesn't make my camera completely stop moving! It does decelerate, but it rarely becomes exactly zero, thus it keeps slowly moving after releasing the key, either towards the direction it was going before or away from it. Can someone explain why doesn't this deceleration work as I intended, (probably because of float inaccuracy, but maybe it's another reason) and how may I proceed to fix it?

  • 2
    Please define _best_ by which means. _"but it rarely becomes exactly zero"_ Does this answer your question: [Is floating point math broken?](https://stackoverflow.com/questions/588004/is-floating-point-math-broken)? You do `float`comparisons wrong. Use [`std::numeric_limits::epsilon`](https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon). – πάντα ῥεῖ Mar 08 '21 at 16:15
  • @πάνταῥεῖ reformulated, I just want to decrement until it becomes zero, but it's not as easy with floats as with Integer values –  Mar 08 '21 at 16:17
  • 3
    Use an *epsilon* to compare against zero. Like `abs(value_that_should_be_zero - epsilon) == 0.0` Also see [How should I do floating point comparison?](https://stackoverflow.com/questions/4915462/how-should-i-do-floating-point-comparison) – Some programmer dude Mar 08 '21 at 16:19
  • 1
    `if (std::fabs(velocity) < tol) velocity = 0.0f;` so check if it's below some numerical tolerance than just assign to `0.0f` instead of doing arithmetic to get it there – Cory Kramer Mar 08 '21 at 16:19
  • I will check out std::epsilon, thanks for the recommendation guys. –  Mar 08 '21 at 16:19
  • 1
    BTW: If you really have an acceleration as input and you want to obtain the velocity you'd rather have to increment it like ```xVelocity -= acceleration * deltaTime;```. Your formulation doesn't make sense physically (sorry to put it so bluntly). But again, I don't know how you have defined acceleration and if it is really representative of a physical acceleration. – ManyQuestions Mar 08 '21 at 16:21
  • Don't use `std::numeric_limits::epsilon`. That's a detail for expert use. In your case, just decide: how close to zero do you want velocity to get before you treat it as zero? And then just do it. See the comment by @CoryKramer. – Pete Becker Mar 08 '21 at 16:51
  • @πάνταῥεῖ: Please do not jump to conclusions about floating-point questions and promiscuously close them as duplicates of that question. This was a math problem, not a floating-point problem. – Eric Postpischil Mar 08 '21 at 19:56

1 Answers1

3

This is not a floating-point accuracy issue unless your acceleration, deltaTime, and velocity are calibrated so that, mathematically, velocity reaches zero exactly after an integer number of steps. More likely, the problem is simply that your velocity is not an exact multiple of acceleration/deltaTime, even allowing for floating-point issues. For example, suppose velocity is 3.7 and acceleration/deltaTime is .2. Then velocity will step through 3.5, 3.3, 3.1,… .5, .3, .1, and −.1.

So velocity never becomes zero because it crosses right through it. When velocity is at .1 units, you can never decelerate by .2 units; an object stops when its velocity reaches zero.

A solution is to detect this and clamp the result to zero:

if (!keys[GLFW_KEY_A] & xVelocity<0.f)
{
    xVelocity = xVelocity >= - deceleration / deltaTime ? 0 : xVelocity + deceleration / deltaTime;
}
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312