Over the years, in fact even in the past year or two I would see on the News some person either getting a bill or a check for some ridiculous amount of money that would be in the form $2020202020… .02.
If you ever see this you know it was (almost certainly) a programming mistake made in COBOL. Most COBOL programmers have made this bonehead mistake and I am no exception.
The problem is caused by the way COBOL programmers typically initialize a record. Given this little program:
identification division. program-id. mistake. data division. working-storage section. * *** Input record, typically maintained on disk/tape somewhere. 01 dr-datarec. 03 dr-name pic x(20). 03 dr-amount pic s9(7)v99, comp-3. * *** print record, sent to a line printer. 01 dt-detail. 03 dt-name pic x(20). 03 filler pic x. 03 dt-amount pic z,zzz,zz9.99. procedure division. move spaces to dr-datarec. move "test" to dr-name. move 100 to dr-amount. move spaces to dt-detail. move dr-name to dt-name. move dr-amount to dt-amount. display dt-detail. stop run.
In this program, dr-datarec is the input record. Normally it would be coming from disk somewhere. For this simple test, I am creating it by hand.
Once the input record is obtained, calculations are done, and the record is then printed using dt-detail.
The problem is in how dr-datarec is created. Note how I move spaces to it to initialize it. This was a common method of initializing a record.
By doing so, all PIC X fields have spaces in them. BUT, all COMP-3 fields are initialized as well, just not to zero. The programmer has to be sure to create valid values for any COMP-3 field. In the test program, this is properly done:
move spaces to dr-datarec. move "test" to dr-name. move 100 to dr-amount.
dr-amount clearly has 100 in it. When I run the program I get:
./mistake test 100.00
What if a coding mistake is made and dr-amount is not initialized properly?
It still contains ASCII spaces. That would be HEX 20 or binary 0010 0000.
COMP-3 stores digits as 4 bit “nibbles”, so a single space would appear as the digits 20. If you have 9 digits, as dr-amount has, it requires 10 nibbles of storage (9 nibbles for the digits and one for the sign) OR 5 bytes.
The MOVE SPACES to DR-DATAREC would result in 5 spaces being stored in the field or the hex value of 2020202020. This is interpreted as 2,020,202.02 if you attempt to use the uninitialized variable.
If I comment out the initialization of dr-amount, I can force this error:
move spaces to dr-datarec. move "test" to dr-name. * move 100 to dr-amount.
Now when I run the program:
./mistake test 2,020,202.02
COBOL 85 introduced the INITIALIZE verb to correct this problem. Rather than moving spaces to the record, you initialize it and it will move spaces to alphanumeric fields and zeros to numeric fields:
* move spaces to dr-datarec. initialize dt-detail. move "test" to dr-name. * move 100 to dr-amount.
./mistake test 0.00
So the next time you see a poor widow charged $2,020,202.02 on their utility bill, you will know exactly how it happened!
The above example assumes an ASCII machine. If run on an EBCDIC-based machine (IBM), SPACE is HEX 40 and you would see $4,040,404.04 instead.