Irrlicht has a few big no-no's in the numerical precision department. I had my first real surprising 3D bug in Starbourne when the camera is tracking the ship and suddenly the whole 3D scene disappears. I instantly suspect it isn't my code (my gut reaction lately and it usually turns out to be correct). I'm suprised niko fell for some of the common things to watch out for... or perhaps one of his contributors didn't realize that the real world sucks.
The fact that I was able to instantly pinpoint a problem of numerical precision as a NAN and even set the breakpoint within the code at the right place scares me. It scares me because I used to be SO scared of not really understanding what is going on in 3D and once I come across a tricky bug how can I fix it? But to have someone better than me fall in silly traps mean that perhaps all I lack is experience... and intellectually I have the capability of "getting it", even if it does take a long time. Perhaps few people do, perhaps people just implement things without a full understanding and get away with it.. Of course, I can live with and almost cherish a few bugs because I love niko's programming style and plus it gives me some real world practice with working with the innards of a 3D engine.
To reiterate, numerical precision issues can occur in many ways. I recommend reading the Collision Detection book for further details by Christer Ericson for more details.
Here is a few common pointers
1. Do not check a floating point for a number directly. Because of numerical accuracy it is often likely that a number will never be completely equal. Addition of large numbers by small numbers often causes precision loss so sometimes, (float)A + (float)B != A + B. So, you must set a tolerance level called epsilon and do a check. So if you want to check if float A == 0.0f, the best way is to actually do
if (fabs(A) < epsilon) { .. }
where say epsilon is small, like 0.000001. You can make it smaller for doubles. Of course, this applies to multiplication, division, and subtraction too.. whenever you are doing operations where the magnitude of the number varys greatly, this is often a big source of loss. Simply stated - floats are a necessary evil. They have the most precision clustered around the origin of the number line (0.0), but the precision decreases as you move to + or -
std::numeric_limits<float> ::max
. So you must be aware that the precision actually changes depending on the number!
2. Watch out for division by ZERO! Seriously, this isn't a game. Divison by zero can happen almost anywhere. So don't do it! Check the dividend and replace it with an epsilon if you must. But then you may have to watch out for overflow! So be careful and try to make sure an equation is stabilized. Do not divide a very big number by a very small number. With planes, you should probably switch to a thick plane implementation as described by Ericson's book.
3. Watch out for fucking inverse tangent function shit. Jesus christ, that's a dangerous function. Watch the values! Make sure they are in range and clamp them if necessary. Watch out for the angles that can produce degenerate values, like near 90 degree increments.
4. Euler angles - Holy fucking shit don't use them. Use axis-angle instead.
Well folks, that is all. I feel the passion exploding in this one. I hate when my 3D scene disappears.