Page 41 - Developer
P. 41
INNER PRodUcT // bRUcE dawsoN
sin(float(pi)) is a very accurate intrinsically safe because it is as these times do not grow as the point exceptions for its scope. By
measure of the error in float(pi). very likely that, even though your game progresses, there will be dropping these into key places
With all that background in GetGameTime() function returns a no problems with bugs that only you can validate the integrity of
place, the two generic comparison double, some junior programmer occur after several days. the code that you control, while
routines I recommend are in Listing will store the result in a float and sidestepping issues in the code
5 (relative and absolute) and you’re back to having bugs that exceptions that you don’t control.
Listing 6 (ULPs and absolute). only occur after hours of gameplay. » All code has bugs. That is the The ideal strategy is to put a
In both cases, if you know Luckily, there is an easy depressing reality of programming. float-exception enabling object in the
that you will not be dealing with solution. The whole problem arises In many cases the difference start function of each thread, and
numbers near zero, then you can because float and double have between shipping on time and then disable exceptions as needed.
use the simpler versions that omit more precision near zero. All we missing Christmas is a matter of Pragmatic constraints might mean
the maxDiff absolute epsilon. have to do is not start our timer at how quickly bugs can be found and that you just enable float-exceptions
zero. If we use a double, and if we fixed. Since games contain huge in your particle system, or just in
FLoats For time anD
Location Fixing rare or difficult-to-reproduce bugs is the most tedious
» Fixing rare or difficult-to-reproduce part of software development, so any design patterns that
bugs is the most tedious part of lead to such bugs should be avoided at all costs. In game
software development, so any development, there are a couple of patterns that lead to bugs
design patterns that lead to such that only occur after many hours of play, or in distant regions
bugs should be avoided at all costs. of maps, and if you don’t take steps to avoid these patterns you
In game development, there are a may have some long nights before certification.
couple of patterns that lead to bugs
that only occur after many hours of
play, or in distant regions of maps, start our timer at 4294967296.0 amounts of floating-point math, you your AI. Baby steps are better than
and if you don’t take steps to avoid (2 to the 32nd power), then our can improve your odds of finding no steps at all. See Listing 7 for the
these patterns you may have some precision will be consistent until quirky bugs if you enable floating- classes I use.
long nights before certification. the timer reaches 8,589,934,592 point exceptions. Enabling floating-
Floating-point numbers are seconds—which won’t happen point exceptions for divide-by-zero, saving FLoats as text
designed to have consistent for more than a century. In overflow, and illegal-operation is » Text-based file formats can be
relative precision across a wide addition to ensuring consistent like adding asserts before and after terribly convenient because they are
range of magnitudes. Or, to put it precision, this technique every floating-point operation. I’ve human-readable and easily editable,
another way, they are designed guarantees that anybody trying used this technique for 20 years and but it is not obvious how to convert a
to have much better absolute to store GetGameTime() in a float it continues to be useful. On a recent float to text in a way that retains the
precision near zero. If you use will immediately hit precision project that was not written to be exact valuesufficient precision.
float or double when this variable problems, and the mistake will float-exception clean, I was able to Printing the exact value of a float
precision is not desirable— quickly be discovered. If you enable float-exceptions in a few key can require over 100 decimal digits.
or when it might even be prefer to return a 64-bit fixed-point systems and then find an illegal Luckily, printing the exact value of
counterproductive—then you can integer, then an extremely large operation that was making particles a float is rarely necessary. What
end up with lots of exciting bugs. number as the start point will disappear, as well as some reading is more useful is to print enough
If you use a float to store catch developers who store the of uninitialized stack memory that digits so that the original value can
elapsed time in your game, that game time in a float or an integer. was triggering various forms of be reconstructed when the text is
means you have more precision You’ll see similar problems undefined behavior. It works. converted back to a float. For this
at the beginning of a game than at occur with player positions and The NaNs and infinities that are purpose it is sufficient to print nine
the end. When your game has been world geometry. Storing player created by exceptional situations can digits of mantissa. Ninety-four
running for 60 seconds, a float that positions in a float means that also cause performance problems, percent of floats can round-trip to
holds the elapsed time will have you have much more precision particularly on the x87 FPU—yet text and back with just eight digits,
.0038 milliseconds of precision. near the origin, and it means another reason to avoid them. but all floats are guaranteed to
Once your game has been running that when you transmit player The complicating factor is that round-trip to text and back with nine
for a day (86,400 seconds) that positions across the network not all code runs float-exception digits. One possible way to do this
same float only has 7.8 milliseconds you are wasting precious bits clean. Some libraries trigger illegal is to use printf(“%1.8e”, f); which
of precision. This is a big enough on transmitting the exponent. operations because they were puts eight digits after the mantissa’s
drop to make it quite likely that you Using floats to store position is carelessly written, and others may decimal point. Perfect.
will have timing precision bugs that probably unavoidable, but when make legitimate use of divide-by- If you’re not sure that you trust
will only show up after the game has transmitting positions you should zero semantics. Either way, any your printf and scanf functions to
been running for a long time. consider using fixed-point. practical way of enabling floating- round-trip floats, you can instead
The simple fix to the timing It is fine to store time point exceptions needs to account easily write code that will iterate
problem is to store time in a 64-bit differences—the time between for this reality. through all of the floats, printing
number—either a double or a events—in a float, and that may The simple technique is to to text, scanning back to float, and
64-bit integer (representing the be necessary for compactly define two C++ classes: one that verifying that nothing is lost. On
elapsed count of microseconds or storing particle lifetimes and other enables a set of floating-point a modern computer, a complete
nanoseconds). These techniques attributes. As long as these times exceptions for its scope, and test takes less than 15 minutes.
both work, but they are not are relatively small, and as long another that disables all floating- There’s no point in wondering
www.gdmag.com 39