开发者

Floating point precision and physics calculations

The gravity Vector2 in my physics world is (0; 0.1).

The number 0.1 is known to be problematic, since "it cannot be represented exactly, but is approximately 1.10011001100110011001101 × 2-4".

Having this value for the gravity gives me problems with collisions and creates quite nasty bugs. Changing the value to 0.11 solves these problems.

Is there a more elegant solution that doesn't require changing the value at all?


Video of the bug

http://www.youtube.co开发者_如何学JAVAm/watch?v=bRynch1EtnE


Source code

http://pastebin.com/jNkqa3sg

The first method (AABBIsOverlapping) checks for intersection betweens two AABB entities. The second method (Update) is called for each body every frame.

I'll try to explain how the Update method works:

  1. Add the gravity acceleration vector to the velocity vector
  2. Create a temp vector (next) and set it to the velocity
  3. Get bodies in the spatial hash in the cells around the current body
  4. If there is an horizontal overlap, resolve it, set next.X and velocity.X to 0 and move the player
  5. If there is a vertical overlap, resolve it, set next.Y and velocity.Y to 0 (or to 0.1 to prevent constant jumping from ceilings) and move the player
  6. After the loop, if there were no overlaps, move the player


In short no. The usual solution is to never check for equality, and always check for a range +- epsilon (very small value).


In physics being unable to represent a number shouldn't matter at all. The only thing that matters in physics is the accumulation of rounding errors.

I assume your problem is related to incorrect epsilon comparisons, or even comparisons without epsilon. But without more information I can't help you. If your code can't deal with small rounding errors it is flawed and needs to be fixed.

You could use Decimal for your math code, which can represent 0.1m exactly. But I don't think that's what you really need since your problem is most likely unrelated to the fact that 0.1 can't be represented exactly in float.


One potential problem I see in your code is that when resolving collisions you move out the body exactly to the collision border. Perhaps you need to move it out an epsilon further.


If non-representability is resulting in bugs in your code then your algorithms are flawed in some way. It's hard to say how they are flawed because you have not shared any details, but using a representable number won't fix it.

In fact 0.11 is not representable either and so if it solves your problem, it does so for some reason other than this.


In short - You can not work with floating-point values on PC as in real life. There's always gonna be precision loss and rounding errors due to limited amount of memory used to store the values within very wide ranges.

Always check for equality with some epsilon which could be half the step between working values, e.g. 0.1:

IsEqual(0.1, 0.2, 0.05) = false
IsEqual(0.1, 0.1001, 0.05) = true
IsEqual(0.1, 0.1499, 0.05) = true

or best precision at given scale and given floating-point format (e.g. 64bit has smaller epsilon than 32bit obviously) (you may need to check with your language for ways to obtaining that value):

IsEqual(0.1, 0.2, BestPrecisionAt(0.1)) = false
IsEqual(0.1, 0.1001, BestPrecisionAt(0.1)) = false
IsEqual(0.1, 0.1499, BestPrecisionAt(0.1)) = false
IsEqual(0.1, 0.1000001, BestPrecisionAt(0.1)) = true
//Where for example BestPrecisionAt(0.1) could be 0.00001

EDIT: You said nothing about bugs you are having. So what is exactly wrong with 0.1? I could only assume that your timestep is not precise enough, your objects speeds allow them to pass through each other inbetween collision checks. Is that correct? If yes - you should increase timestep resolution and/or check for collisions earlier.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜