Blog Home

Double, Double, Trouble

Tue Jun 19, 2012
While procrastinating (avoiding writing this blog entry for as long as possible), I debugged an interesting problem.  This gives me something to talk about here.  What follows might be simple or obvious, but I find that considering tiny details very carefully is a good way to improve the quality of the code I write.  Consider the following code snippets
 
sphere* make_sphere( double radius, double x, double y, double z);

 

void do_something( /* ... */ )
{

// ...

sphere* my_sphere = make_sphere(10,0,0,1);
}

and

class position
{
// ...
public:
position( double x, double y, double z);
//...
};

sphere* make_sphere( position const& p, double radius);

void do_something( /* ... */ )
{

// ...
position center( 0,0,1);
sphere* my_sphere = make_sphere();
}

With the second version of the code, you actually need to have a class structure defining your objects (which requires more code), but strong type checking can help you.  There is also an annoyance with the second version of the code that you may have to write code converting between various types of geometric operators.  This (having well thought out basic types for mathematics) is one area where CGM does particularly well.
 
The actual bug I looked at was closely related (class names changed to protect the guilty).
class nifty_curve_calculator

{

// ...

public:

nifty_curve_calculator( double convergence_epsilon, double fitol_desired, ...);

//..

};

In nifty_curve_calculator, exact points on a curve are calculated to convergence_epsilon.  The nifty_curve_calculator then concatenates a bunch of exact points on the curve into a bspline fit for the curve.  The fitol is the requested distance of the bspline from the exact curve being calculated.  The two tolerances mean completely different things, but the compiler will happily accept code which switches the two tolerances.  In the case I looked at today, the two parameters were swapped which resulted in code that worked most of the time, but caused a hang with more poorly behaved geometry.  We should expect that convergence_epsilon is a lot smaller (10^3 times smaller or more) than the fitol_desired.
 
There is a whole constellation of bugs like this that can be avoided by making a careful object model.  A simple way to improve type checkability is to avoid argument lists where convertable types are right next to each other.  Avoiding void* arguments like the plague also fits into this line of design improvement.  An additional help is to only require arguments in a constructor which are absolutely mandatory and use get/set methods to control the other parameters.  
 
One area where I run into problems with this is writing code (e.g., for MESH_MANAGERS) where large objects are stored using arrays of indices into other arrays.  If everything has type int (or size_t if that is how you roll), then compiler type checking doesn't help much.  Pointers are slightly better for this, but then you get into ownership issues.  I really wish you could do typedefs that aren't convertable to each other but have the same operations as integers.
 
Does you have any suggestions or comments for improving type checking in geometric code?

Tags: 3D Modeling