0

Goes without saying: Crash safety

Page 1 | Page 2 | Page 3 | Page 4

Division by zero

Another common cause for crashes is devision by zero. In a nutshell:

You shall not divide by zero!

In many cases, it is obvious if the code is prone to Division By Zero crashes:

Real Ratio(Real x)
{
  return RCO 1.0 / x;
}

This function will – of course – crash when called with 0.0 as parameter value.
You have to check x for 0.0 first and only perform the devision if x is not 0.0.

Real Ratio(Real x)
{
  if (x == RCO 0.0) return RCO 0.0;
  return RCO 1.0 / x;
}

A lot better… you might think. But keep in mind that floating point values only have a limited precision. A value might be so small that is crashes even if it’s not exactly 0.0.
Therefore, it is better to use a function like CompareFloatTolerant() from the CINEMA 4D API.

Real Ratio(Real x)
{
  if (CompareFloatTolerant(x, RCO 0.0)) return RCO 0.0;
  return RCO 1.0 / x;
}

In practice, if you have a more complex calculation that is time-critical (e.g. a shading algorithm), you will want to ensure valid values before the calculation starts. Calling CompareFloatTolerant() all the time might have an impact on the performance (depending on how your algorithm works and in which context it is called).

However, there are cases where it is not as obvious that a division by zero might occur.

For example, we have a GeUserArea, and we want to draw a grid of items into it. The width of the grid cells is fixed to 150 pixel, so we can calculate the number of grid columns from the UserArea’s width and the column width, and the number of grid rows from the number of columns and the number of items to draw.

Bool MyUserArea::GetMinSize(LONG& w, LONG& h)
{
  w = 100;
  h = 100;

  return TRUE;
}

Bool MyUserArea::DrawMsg(LONG x1, LONG y1, LONG x2, LONG y2, const BaseContainer& msg)
{
  // Calculate number of columns from width of UserArea and column width
  _columns = GetWidth() / 150;

  // Calculate number of rows from number or columns and number of items
  _rows = _items.GetCount() / _columns;

  // ...
}

Where could this code crash? Of course, you might say, I can’t just divide by _columns, because _columns can be zero. But wait, why would _columns be zero? We calculate its value by dividing the width of the GeUSerArea by 150. It could only be zero, if the UserArea’s width is zero, too. And that, again, should never happen, since we have set the UserArea’s minimum size to 100×100 pixel. Correct? No, wrong. In case, DrawMsg() is called before GetMinSize() under some weird circumstances, the width might still be zero. That would make _columns zero, too. And then it would crash.

This is just one of many examples where rare circumstances can lead to crashes that can be very hard to track down. Let’s remember the first sentence: You shall not divide by zero! Therefore, always check for zero before any division, even if there’s only the slightest chance that the divisor could be zero.

Bool MyUserArea::DrawMsg(LONG x1, LONG y1, LONG x2, LONG y2, const BaseContainer& msg)
{
  // Calculate number of columns from width of UserArea and column width
  _columns = GetWidth() / 150;

  // Zero check
  if (_columns == 0)
    _columns = 1;

  // Calculate number of rows from number or columns and number of items
  _rows = _items.GetCount() / _columns;

  // ...
}

Or shorter:

Bool MyUserArea::
{
  // Calculate number of columns from width of UserArea and column width
  _columns = GetWidth() / 150;

  // Calculate number of rows from number or columns and number of items
  _rows = _items.GetCount() / (_columns == 0 ? 1 : _columns);

  // ...
}

Frank Willeke

worked with computers since more than 20 years | got hooked on computer graphics back on the AMIGA | started programming at the age of 13 | relesed some successful plugins for cinema 4d | started working for maxon computer gmbh in 2009 | now contributing to cinema 4d as a senior developer

making electronic music since 1993 | playing the electric guitar since 1995 age of 14 | first live gigs in 1997 | playing the bass guitar 2005 | playing keyboards and synths since 2012

experimenting with photography since 2003

Leave a Reply

Your email address will not be published. Required fields are marked *