Click here to Skip to main content
13,199,571 members (62,131 online)
Click here to Skip to main content
Add your own
alternative version


17 bookmarked
Posted 14 Feb 2003

Diary Of A Coder--Marc's Boatyard Bug

, 14 Feb 2003
Rate this:
Please Sign up or sign in to vote.
Marc bares all in a discussion of a bug involving the atof function.


No, there's nothing wrong with the atof function. What's wrong is my code that uses it. And I thought this little story would be amusing and interesting to some of you, so I decided to up my article count by one more article and write this little exposition, exposing myself to you, the dear reader, to your ridicule and guffaws.

Now, there are a lot of excellent articles on CP, to which I of course have contributed several, but it seems that there are few (if any--I certainly didn't find any, but then again, I didn't look very hard) articles on our day-to-day trials and tribulations and the stupid things we do. So I am yet again setting a precedent at CP by writing about something everyone else is either too ashamed, scared or smart to write about. And in between the lines, you may discover something that touches your soul (or takes you for a spin on the porcelain bus).

Some bugs aren't actually that stupid and illustrate that even with the best of unit testing, we can't test for every contingency. This is a story of how I screwed up a piece of code that worked fine for a month, until one day we got an invoice from a vendor that was, well, too large an amount for my software to handle.

Setting The Stage

Some of you know that I do some contract work for two boatyards in the "tax me" state, otherwise known as Connecticut. Everything in Connecticut is taxed, and there is fees for everything--even the beaches. In fact, Governor Rowland, who just got re-elected, is closing half of the state's Department of Motor Vehicles offices because of a project $1,500,000,000 deficit for next year. Now, none of this has to do with my bug or this article, except for a particular feature of that very large amount. But I get ahead of myself.

One of the accounting functions requires verifying a vendor's invoice against the purchase order and entering the freight charges into the system. At that point, the PO can be closed and the customer(s) are billed for the parts and any freight charges incurred by the boatyard. So, how do freight charges get distributed, you may ask? Well, the freight charges are distributed based on the percent of an item's quantity*cost as part of the entire invoice amount. There's no other way of doing it because we don't know the weight of things, which is really how freight is charged. So, it means that a guy buying a $5,000 radar is going to pay for most of the freight when there's another guy buying a 500lb block of lead ballast for 5 bucks on the same invoice. Nobody said life is fair. And nobody said that my examples have any relationship to reality.

Now, note in this screen shot, which is inspecting the problem after it happened, that, while the vendor cost is $162.88 for this part (which by the way is a 12V pump), my software has calculated that, factoring in the 37.56 of freight charges, the part actually costs a whopping $1,304.86!

Oh my. This is not good. The boatyard maintains an "adjusted cost" for every inventory item that is a moving average of the actual price of the part plus freight charges. The moving average comes into play each time the part is purchased. The adjusted cost is recalculated to 4/5th's of the current cost plus 1/5th of the new cost. This way, we can slowly modify our inventory cost. Now, for this $162 part, I've added over $1300 in inventory value.

Needless to say, our accountant is not happy. With me.

The GUI and Data Acquisition

So, let's trace into how this is happening.

First off, the GUI that is displaying the information on the screen looks like this (this is a piece of it):

dlg nosize true
dlg caption "Process Vendor Invoice"
font ("MS Sans Serif", 8)
STATIC s1 at (0, 20) size (100, 15) font 
  ("MS Sans Serif", 8, B) caption "Select Part:" end.
LISTCONTROL lcCheckItems at (0, 35) size (465, 120)
    storage rcvCheckIdx
    list storage rcvCheckList
    header ("OK?":40C,
                "Vend. Part #":80,
                "Cost $":65R,
                "Total $":70R,
    with options (grid editable)
    on selection ScriptMgr.RunMacro(dp_macroName, SelectRcvCheckItem)
    on double click ScriptMgr.RunMacro(dp_macroName, RcvShowPartInfo2) end.
BUTTON btnClosePO at (480, 220) size (100, 20) caption "Close PO"
        on selection ScriptMgr.RunMacro(dp_macroName, ClosePO) end.

Doesn't look like any GUI you've ever seen, does it? Well, that's because it's part of my Application Automation Layer for C++/MFC that I use for all my C++ projects. I'm sure the intelligent reader can correlate the specification for the LISTCONTROL to the GUI presented in the screenshot.

Now, here's the database query that loads the rcvCheckList matrix:

DBMgr.QueryMultiRow(dp_DB, MyDB, "select
    'per '+a.BASE_QTY+' '+h.NAME,
    'Stocking Unit is per '+i.NAME,
    PO_ITEM a,
    VENDOR c,
    UNIT h,
    UNIT i
    b.ID=a.PARTNUM_ID and d.ID=a.PO_ID and c.ID=d.VENDOR_ID and d.ID={rcvID} and
        g.ID=a.WO_ID and f.PARTNUM_ID=a.PARTNUM_ID 
        and VAL(f.BASE_QTY)=VAL(a.BASE_QTY) and
        f.VENDOR_ID=d.VENDOR_ID and a.UNIT_ID=f.UNIT_ID and
    by a.ID", rcvCheckList)

followed by some adjustments to the data and the list control:

DataMatrix.Iterate(dp_iterateMatrix, rcvCheckList, i, AdjustCheckList, 0)
Number.CommaFormat(dp_formatNumber, rcvCheckTotal, {rcvCheckTotal}, %.2lf)
      ReceiveCheckTab, lcCheckItems, 0, YESNO)
WinMgr.UpdateAllControls(dp_viewName, ReceivePartsTab)
Math.RPN(dp_RPN, "{rcvCheckTotal} {rcvCheckList(7, {i})} + rcvCheckTotal STO")
Number.Format(dp_formatNumber, rcvCheckList(6, {i}), {rcvCheckList(6, {i})}, %.4lf)
Number.Format(dp_formatNumber, rcvCheckList(7, {i}), {rcvCheckList(7, {i})}, %.2lf)
Number.Format(dp_formatNumber, rcvCheckList(8, {i}), {rcvCheckList(8, {i})}, %.4lf)
Number.Format(dp_formatNumber, rcvCheckList(9, {i}), {rcvCheckList(9, {i})}, %.4lf)
  rcvCheckList(10, {i}), {rcvCheckList(10, {i})}, %.4lf)
  rcvCheckList(11, {i}), {rcvCheckList(11, {i})}, %.4lf)

For those of you not familiar with my AAL script language (and that's ALL of you), things in curly braces {} return their value. For example, {rcvCheckList(6, {i})} returns the value in the 6th column of the i'th row of the rcvCheckList matrix. "RPN" means "Reverse Polish Notation", which is a stack based processor, made famous by Hewlett-Packard calculators.

Now, have you, the astute reader, already realized my stupid mistake and are laughing at me??? If not, read on.

Closing The PO

When the "Close PO" button is pressed, the program begins executing the following script:

   ClosePO, {rcvID}, -1, "Please select a PO.")
DBMgr.QuerySingleRow(dp_DB, MyDB, 
  "select FREIGHT <totalFreight> from PURCHASE_ORDER where ID={rcvID}")
  rcvCheckList, i, GetLineItemFreight, 0)

which verifies that there's a PO on which to operate. It then gets the current purchase order freight charges, and then it iterates through the items on the PO, skipping items already processed (which handles backorders), and then calculates the freight charges that should be distributed for the specific line item:

; skip items already billed (handles backorders)
DBMgr.QuerySingleRow(dp_DB, MyDB,
     "select PROCESSED <billed> from 
      PO_RECEIVE where PO_ITEM_ID={rcvCheckList(1, {i})}
      order by RECEIVE_DATE desc, RECEIVE_TIME desc")
ScriptMgr.VerifySelection(dp_verifySelection, GetLineItemFreight, {billed}, 1, "")

; Total = SUM(qty*cost), items = {rcvCheckTotal}
; for each line item: ItemFreight = TotalFreight * (qty*cost)/TotalCost

Math.RPN(dp_RPN, "{totalFreight} 
  {rcvCheckList(7, {i})} {rcvCheckTotal} / * itemFreight STO")

     7,i={rcvCheckList(7, {i})},

For the first item in our list:

purchase order total (rcvCheckTotal) = 1,987.03

According to the calculation, this should assign a freight charge of $3.08 to the item. Instead, we're getting a freight charge of $6117.77, as illustrated by our little debugging message! It's as if the PO total = $1, and we're just multiplying freight and item cost! (clue!) Next stop, the RPN function.

The RPN Function

The RPN function is really trivial and doesn't deserve commenting. All operators are derived from a base class, and the method that actually performs the operation, Go is a virtual method.

void RPN::Go(void)
    while (rpn[0])
        AutoString token=GetToken(rpn);
        RPNOperator* oper=FindOperator(token);
        if (oper)
            AutoString result;
                case 1:
                case 2:
                    result=oper->Go(v1, v2);

            if (result != "")

Now let's take one particular function, in this case the division function:

AutoString RPNOperatorDivide::Go(const AutoString& v1, const AutoString& v2)
    return AutoString(atof(v2) / atof(v1));

Wow. What could be simpler? Tracing into this, we discover that:


But wait!!!

The BUG!!!

The atof function is too simple! Given "1,987.03", it returns 1.0000!!!

Why didn't we see this before? Well, for one, this code was tested on invoices less than $1,000. Secondly, I later on modified the script to comma format the number. Thirdly, the boatyard doesn't get too many invoices over $1,000 and therefore we went along for quite a while before this problem occurred.

The Fix

Believe it or not, I've had exactly this same bug before, but instead of fixing it right, I chose a different solution specific to the problem at hand and thus got bit by the same bug again. The general solution is to fix it in the RPN code by stripping out the commas. Of course, this necessitated changing the signature of the methods as well, because they were const references before.

AutoString RPNOperatorDivide::Go(AutoString v1, AutoString v2)
    v1.Replace(",", "");
    v2.Replace(",", "");
    return AutoString(atof(v2) / atof(v1));


The beauty of this, and the AAL technology, is that now this problem is forever fixed in all my applications written with the AAL. And because the RPN functions are in the mathAtmtn.dll, I only need to update the DLL instead of recompiling every application I've written with this technology.

Now, there IS another bug in this code. Care to guess what it is?


My girlfriend is giving me a real hard time. I told her I didn't spell check this article, and she said "Marc Clifton! how dare you! After you get on everyone else's case about spell checking!". Of course, then she immediately found a spelling error! "You won't get a 5", she said.


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Marc Clifton
United States United States
Marc is the creator of two open source projects, MyXaml, a declarative (XML) instantiation engine and the Advanced Unit Testing framework, and Interacx, a commercial n-tier RAD application suite.  Visit his website,, where you will find many of his articles and his blog.

Marc lives in Philmont, NY.

You may also be interested in...

Comments and Discussions

GeneralGreat Article Pin
hammackj30-Jun-04 18:02
memberhammackj30-Jun-04 18:02 
GeneralPictures, please Pin
Anonymous16-Feb-03 13:30
sussAnonymous16-Feb-03 13:30 
GeneralRe: Pictures, please Pin
Marc Clifton16-Feb-03 13:43
memberMarc Clifton16-Feb-03 13:43 
GeneralId give you a 10 if .... Pin
Garth J Lancaster16-Feb-03 10:32
memberGarth J Lancaster16-Feb-03 10:32 
GeneralRe: Id give you a 10 if .... Pin
Marc Clifton16-Feb-03 11:44
memberMarc Clifton16-Feb-03 11:44 
GeneralRe: Id give you a 10 if .... Pin
Garth J Lancaster16-Feb-03 11:48
memberGarth J Lancaster16-Feb-03 11:48 
QuestionBug? Pin
Ted Ferenc16-Feb-03 6:43
memberTed Ferenc16-Feb-03 6:43 
AnswerRe: Bug? Pin
Marc Clifton17-Feb-03 11:40
memberMarc Clifton17-Feb-03 11:40 
GeneralRe: Bug? Pin
Ted Ferenc17-Feb-03 12:20
memberTed Ferenc17-Feb-03 12:20 
GeneralRe: Bug? Pin
DavidCrow21-Aug-03 10:35
memberDavidCrow21-Aug-03 10:35 
GeneralRe: Bug? Pin
Ted Ferenc21-Aug-03 12:17
memberTed Ferenc21-Aug-03 12:17 
GeneralWhats happens when... Pin
Giles16-Feb-03 2:57
memberGiles16-Feb-03 2:57 
GeneralRe: Whats happens when... Pin
Marc Clifton16-Feb-03 3:07
memberMarc Clifton16-Feb-03 3:07 
Yes, the AutoString is derived from CString, so it replaces all occurances.

It's actually more insidious (dang, had to look that one up!) than that.

Big Grin | :-D


Help! I'm an AI running around in someone's f*cked up universe simulator.
Sensitivity and ethnic diversity means celebrating difference, not hiding from it. - Christian Graus
Every line of code is a liability - Taka Muraoka
Microsoft deliberately adds arbitrary layers of complexity to make it difficult to deliver Windows features on non-Windows platforms--Microsoft's "Halloween files"

QuestionWhats the other bug? Pin
leppie15-Feb-03 23:01
memberleppie15-Feb-03 23:01 
AnswerRe: Whats the other bug? Pin
Giles16-Feb-03 3:02
memberGiles16-Feb-03 3:02 
GeneralRe: Whats the other bug? Pin
PJ Arends16-Feb-03 7:57
memberPJ Arends16-Feb-03 7:57 
GeneralRe: Whats the other bug? Pin
Giles16-Feb-03 8:34
memberGiles16-Feb-03 8:34 
AnswerRe: Whats the other bug? Pin
Marc Clifton17-Feb-03 11:42
memberMarc Clifton17-Feb-03 11:42 
GeneralWell Written Pin
Dan_P15-Feb-03 20:22
memberDan_P15-Feb-03 20:22 
GeneralThe best and worst of bugs Pin
Peter Hancock15-Feb-03 20:20
memberPeter Hancock15-Feb-03 20:20 
GeneralGood Job Pin
Nick Parker15-Feb-03 17:33
editorNick Parker15-Feb-03 17:33 
GeneralWhat!! Pin
Kant15-Feb-03 17:31
memberKant15-Feb-03 17:31 
GeneralI agree with Anders Pin
Jörgen Sigvardsson15-Feb-03 14:41
memberJörgen Sigvardsson15-Feb-03 14:41 
GeneralRe: I agree with Anders Pin
James T. Johnson15-Feb-03 14:58
memberJames T. Johnson15-Feb-03 14:58 
GeneralYou just got your 5 Pin
Anders Molin15-Feb-03 14:32
memberAnders Molin15-Feb-03 14:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.171020.1 | Last Updated 15 Feb 2003
Article Copyright 2003 by Marc Clifton
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid