The truncation that underpaid interest for eleven years
I have been asked a few times on here what the worst bug of my career was. It was not mine, but I was the one who found where it lived, so I will tell it.
In 2009 a retired schoolteacher walked into one of our branches with a passbook and a pocket calculator. She had been computing her own savings interest every month since 1998, out of habit, and for eleven years our figure had been a few cents lower than hers. Some months one cent. Some months nine. The branch manager, to his great credit, did not send her away. He sent the passbook to us.
It took me four days. In 1998, during the Y2K remediation, a programmer had reworked the daily accrual paragraph for the date logic. The old code accumulated the daily interest in a work field with six decimal places and rounded once, at month end. The new code stored each day's interest in a field with two decimal places before adding it to the monthly total.
* WORKING-STORAGE, AS CHANGED IN 1998
01 WS-DAILY-INT PIC S9(7)V99 COMP-3.
01 WS-MONTH-INT PIC S9(7)V99 COMP-3.
* PROCEDURE DIVISION
COMPUTE WS-DAILY-INT = WS-BALANCE * WS-RATE / 365
ADD WS-DAILY-INT TO WS-MONTH-INT
For the young people reading: COBOL does exactly what the PICTURE clause tells it. COMPUTE truncates. It does not round unless you write ROUNDED. So every single day, every account lost whatever fraction of a cent fell past the second decimal. Thirty days a month. Thirty-eight thousand passbook accounts. Eleven years. It came to a little over half a million dollars, a fraction of a cent at a time.
The fix was three lines:
01 WS-DAILY-INT PIC S9(7)V9(6) COMP-3.
01 WS-MONTH-INT PIC S9(7)V9(6) COMP-3.
01 WS-POST-INT PIC S9(7)V99 COMP-3.
COMPUTE WS-DAILY-INT = WS-BALANCE * WS-RATE / 365
ON SIZE ERROR PERFORM 9100-ACCRUAL-OVERFLOW
END-COMPUTE
ADD WS-DAILY-INT TO WS-MONTH-INT
* AT MONTH END, ROUND ONCE
COMPUTE WS-POST-INT ROUNDED = WS-MONTH-INT
The remediation was not three lines. It was eight months, thirty-eight thousand letters, a refund with interest on the interest, and a conversation with the examiners that I was not invited to and did not want to be.
What I took from it, and still tell anyone who will listen:
- Know what your language does with the digits it cannot keep. COBOL truncates. Other languages do other things. None of them ask you first.
- Size errors are silent unless you say ON SIZE ERROR. Say it.
- Any change that touches a money field needs a reconciliation against an independent calculation. Not the same program run twice. A different method.
- When a customer says your number is wrong, assume for one day that she is right.
The schoolteacher got her refund and a letter from the president of the bank. She wrote back to say her figures had been correct all along, which they had. I kept a copy of that letter in my desk until we moved buildings.
Get the best of Dynamo Geeks weekly
One email each Monday with the most useful posts. No spam; unsubscribe in one click.
Comments
Sign in to join the conversation.
Sign in to comment