2

I was just writing some code for a fairly simple physics simulation program where the user could alter gravitational acceleration and I wanted to limit the minimum gravity value to some value (to prevent 0 and negative gravity).

I ended up with something like this:

if(g_accel > 0.1) {
    g_accel -= 0.1;
}

Where g_accel is set to 1.0 at the start of the program. However it would still allow the user to go below 0.1 to a value something like 1.38778e-016. My solution to the problem was to instead check:

if(g_accel > 0.1+std::numeric_limits<double>::epsilon()) {
    g_accel -= 0.1;
}

Aside from the use of magic constants, my problem is that I have no idea why this is necessary, and, furthermore, I don't really understand why epsilon() is useful or how/when it is used.

I was also looking at some code a moment ago that looked like this:

inline void Vector2D::Normalize()
{ 
  double vector_length = this->Length();

  if (vector_length > std::numeric_limits<double>::epsilon())
  {
    this->x /= vector_length;
    this->y /= vector_length;
  }
}

What's going on with std::numeric_limits::epsilon()?

masrtis
  • 1,282
  • 17
  • 32
Alden Bernitt
  • 119
  • 1
  • 1
  • 7
  • With floating point types you try to store an infinite number of decimals in a finite number of bits. That's impossible so we get rounding errors where a value that should be e.g. `1.0` instead might be `0.9999998674` or something. Also see e.g. [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken). – Some programmer dude Mar 15 '17 at 03:14
  • 1
    The [documentation](http://en.cppreference.com/w/cpp/types/numeric_limits/epsilon) and [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) should help. – Jesper Juhl Mar 15 '17 at 06:38

1 Answers1

3

From the cppreference page on std::numeric_limits<T>::epsilon, std::numeric_limits<T>::epsilon() returns the smallest such T that 1 + std::numeric_limits<T>:epsilon() is not equal to 1. This value is only useful if std::numeric_limits<T>::is_integer() returns false. The name for this value is the machine epsilon.

Because the representation of floating point numbers results in bigger gaps between representable numbers as the numbers get farther away from 0 on the number line, the return value of epsilon() needs to be scaled to work with the result. Real Time Collision Detection by Christer Ericson describes some of the math behind the scaling. The recommendation he suggests in his book is that the epsilon value that should be used when performing floating point equality checks should be the square root of the machine epsilon.

Bruce Dawson's blog series, which I linked to above, is a great resource for learning about the complexities of floating point arithmetic. I highly recommend reading all of the posts in the series if you'd like to continue implementing physics simulations.

masrtis
  • 1,282
  • 17
  • 32