Signed zoned decimal and overpunch
You met zoned decimal (USAGE DISPLAY) numbers early on: one byte
per digit. Now look at the bytes. On z/OS each EBCDIC digit byte has two
halves (nibbles): a zone on the left and the digit on the right.
The digit 7 is X'F7': zone F, digit 7.
A signed field has no byte for the sign. Instead the sign replaces the zone of the last byte:
| Zone | Meaning |
|---|---|
C |
positive |
D |
negative |
F |
unsigned (treated as positive) |
So PIC S9(3) holding -123 is X'F1F2D3'. This trick comes from
punched cards, where the sign was an extra hole punched over the last
digit, hence the name overpunch.
Why letters appear
X'D3' is not a digit character. In EBCDIC it is the letter L. So when
you browse a dataset in ISPF, or when a file is converted to text and
downloaded, signed amounts end in letters and braces:
| Last digit | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
|---|---|---|---|---|---|---|---|---|---|---|
positive (Cx) |
{ |
A |
B |
C |
D |
E |
F |
G |
H |
I |
negative (Dx) |
} |
J |
K |
L |
M |
N |
O |
P |
Q |
R |
0012345 with an unsigned last digit is +123.45 in S9(5)V99;
001234E is also +123.45; 001234N is -123.45. Memorise the table.
You will see it in almost every file you look at.
GnuCOBOL is different
On an ASCII machine GnuCOBOL leaves positive values as plain digits and
marks negatives with its own codes (p to y). Run this:
IDENTIFICATION DIVISION.
PROGRAM-ID. OVERPNCH.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-Z PIC S9(3) VALUE -123.
01 WS-Z-BYTES REDEFINES WS-Z PIC X(3).
01 WS-TEXT PIC X(7) VALUE "001234E".
01 WS-AMT REDEFINES WS-TEXT PIC S9(5)V99.
PROCEDURE DIVISION.
DISPLAY "WS-Z = " WS-Z ", STORED AS " WS-Z-BYTES
", HEX " FUNCTION HEX-OF(WS-Z)
IF WS-AMT NUMERIC
DISPLAY "001234E IS NUMERIC HERE"
ELSE
DISPLAY "001234E IS NOT NUMERIC HERE"
END-IF
STOP RUN.
WS-Z = -123, STORED AS 12s, HEX 313273
001234E IS NOT NUMERIC HERE
DISPLAY WS-Z edits the value for you (-123), but the stored bytes
are 12s. And a mainframe-style 001234E is not valid zoned data to
GnuCOBOL, so you must decode it yourself. That's the exercise: it is
exactly the job you get when a mainframe extract lands on a Linux server.
The sign is part of the data
Move a signed field to a PIC X field and you move the overpunched
byte, not a minus sign. For printing, always move numbers to an
edited picture such as ----,--9.99.
On the job
When a downloaded file's amounts end in {, } or letters, don't
"clean up" the letters: they are the sign. Ask whether the file should
have been produced with SIGN IS LEADING SEPARATE (an explicit + or
- byte), which is the safe format for exchanging numbers as text.
Your task
A mainframe extract of account adjustments was transferred to a Linux
server as text. The amounts were PIC S9(5)V99 signed zoned decimal, so
their last character is now an overpunch letter or brace. Decode them.
Input, DD name AMOUNTS, line sequential:
| Field | Picture | Positions |
|---|---|---|
AMT-ACCT |
X(6) |
1–6 |
FILLER |
X |
7 |
AMT-TEXT |
X(7), meaning S9(5)V99 with overpunch |
8–14 |
The last character of AMT-TEXT gives the last digit and the sign:
{ A–I are +0 to +9, } J–R are -0 to -9, and a plain digit
0–9 is positive. The first six characters must be digits.
For each record display either the account, a space and the amount as
----,--9.99:
ACC004 -1.21
or, if the last character is not in the table or any of the first six is not a digit:
ACC006 INVALID 00100X1
After the last record:
VALID: 006 INVALID: 001
NET: 967.14
(NET is the sum of the valid amounts, ----,---,--9.99.)