Game Design, Programming and running a one-man games business…

An impossible bug. ARGGGH

Check out this code from production line for displaying pie charts of expenses.  I declare arrays of float totals for each of 3 pie charts.

 float totals[NUMPIES];
 totals[PIE1] = 0;
 totals[PIE24] = 0;
 totals[PIEALL] = 0;

 

Then I declare a 2-dimensional array of floats which I fill with some data. As I build up those amounts I also update the totals:

float amounts[NUM_FINANCE_CATEGORIES][NUMPIES];

for (int r = 0; r < NUM_FINANCE_CATEGORIES; r++)
 {
 if (r != FC_CAR_SALES)
 {
 amounts[r][PIE1] = SIM_GetFinanceRecords()->GetAmount(1, (FINANCE_CATEGORY)r);
 amounts[r][PIE24] = SIM_GetFinanceRecords()->GetAmount(24, (FINANCE_CATEGORY)r);
 amounts[r][PIEALL] = SIM_GetFinanceRecords()->GetAmount(-1, (FINANCE_CATEGORY)r);

totals[PIE1] += amounts[r][PIE1];
 totals[PIE24] += amounts[r][PIE24];
 totals[PIEALL] += amounts[r][PIEALL];
 }
 }

 

Then some simple resetting of data, which is irrelevant for this bug, and a check that I am not about to divide by zero:

Pies[PIE1]->Clear();
 Pies[PIE24]->Clear();
 Pies[PIEALL]->Clear();

if (totals[PIE1] <= 0 || totals[PIE24] <= 0 || totals[PIEALL] <= 0)
 {
 return;
 }

 

Then the final code:

 //now create
 for (int r = 0; r < NUM_FINANCE_CATEGORIES; r++)
 {
 if (r != FC_CAR_SALES)
 {
 for (int p = 0; p < NUMPIES; p++)
 {
 float perc = amounts[r][p] / totals[p];
 assert (perc >= 0 && perc <= 1.0f)

Hold ON STOP!. How can that assert ever trigger? EVER? (it does for some people). Its driving me mad :D. It can ONLY trigger if one of the amounts in the array is less than 0% or more than 100% of the total. I KNOW that the total is greater than zero, so the amount must be greater than zero. The total is only comprised of the sum of the amounts. There is no way these numbers can be out of synch, PLUS, they are all floating point vars so there is no rounding going on… or is it maybe a tiny tiny quantizing thing? (I’ve update the game with extra debug data for this error so I’ll find out soon). Surely thats the only explanation, and perc is something like 1.000000001%?

 

 


7 thoughts on An impossible bug. ARGGGH

  1. > they are all floating point vars so there is no rounding going on

    Oh I wish. It’s the other way around. Floating points can have rounding down (or up) for even seemingly trivial amounts.

    Try this:

    printf (” %.20f \n”, 3.6);

    And see what happens.

    This is why people don’t use floats for things like money transfers.

    I’m using floats for my collision detection library and they are a pain. I’m this close to giving up on them.

    More info here:

    https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html

    1. Yeah I also had the same thought about floating point precision. Essentially, *always* assume they’re horribly inaccurate, especially for something like this. You should be working with integer math (fixed point?) and large integers at that …though if your numbers are large enough that they won’t fit even in 64-bit then that’s a problem and you need to get more nitpicky (clamp when numbers are close enough to the range and assert only if they stray too much?).

      On that note: 32-bit floats have 24 bits worth of magnitude and drop all precision bits after that. Using 32-bit integers can actually *better* here :P So you may want to consider 64-bit floats first (which have 53 bits worth of magnitude).

  2. Things I’d check, in order:

    – NUMPIES is larger than 3
    – One of the amounts is negative
    – One of the amounts is NaN
    – One of the amounts is super large (thus triggering precision shenanigans)
    – One of the totals is super small but not zero (thus triggering precision shenanigans)

  3. At ye olde game dev studio I worked at, we had a lovely assert-like verify macro that took a condition and a formattable string. It was very useful when dealing with floating point issues since the assert on its own as always a pain when it occurred on a testers machine without visual studio installed.

    So you could then do:
    verify( perc >= 0 && perc <= 1.0f, "perc value (%f) out of bounds", perc);

  4. Yeah it was a quantize thing, like -0.00 (ARGH). I just wanted to SEE it happen in a bug report before I could really tell myself it was harmless :D

Comments are currently closed.