Packed decimal at the byte level
In an earlier module you learned that COMP-3 stores two digits per
byte. On the job you need to go one step further: look at raw bytes in a
dataset or a dump and read the number straight off them.
The layout
A packed field is a row of nibbles (half-bytes). Each nibble is one
decimal digit, 0–9, and the very last nibble is the sign:
| Sign nibble | Meaning |
|---|---|
C |
positive |
D |
negative |
F |
unsigned (positive) |
PIC S9(7)V99 COMP-3 has 9 digits + 1 sign = 10 nibbles = 5 bytes.
The value -1234.50 is stored as the digits 000123450 followed by D:
X'00 01 23 45 0D'
The decimal point is not stored anywhere. V99 exists only in the
program, which is why two programs with different copybooks can read the
same bytes as different amounts.
Run this to see the bytes of three packed fields:
IDENTIFICATION DIVISION.
PROGRAM-ID. PACKED.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-BALANCE PIC S9(7)V99 COMP-3 VALUE -1234.50.
01 WS-RATE PIC S9(4) COMP-3 VALUE +123.
01 WS-COUNT PIC 9(3) COMP-3 VALUE 7.
PROCEDURE DIVISION.
DISPLAY "BALANCE " WS-BALANCE " X'"
FUNCTION HEX-OF(WS-BALANCE) "' "
FUNCTION LENGTH(WS-BALANCE) " BYTES"
DISPLAY "RATE " WS-RATE " X'"
FUNCTION HEX-OF(WS-RATE) "' "
FUNCTION LENGTH(WS-RATE) " BYTES"
DISPLAY "COUNT " WS-COUNT " X'"
FUNCTION HEX-OF(WS-COUNT) "' "
FUNCTION LENGTH(WS-COUNT) " BYTES"
STOP RUN.
BALANCE -0001234.50 X'000123450D' 5 BYTES
RATE +0123 X'00123C' 3 BYTES
COUNT 007 X'007F' 2 BYTES
S9(4) still needs 3 bytes: 4 digits + sign is 5 nibbles, and the
compiler pads with a leading zero nibble. That is why mainframe shops
declare packed fields with an odd number of digits: S9(7)V99,
S9(5), S9(15)V99. Nothing is wasted.
Packed bytes are identical on z/OS and in GnuCOBOL. Only the text around them differs (EBCDIC vs ASCII).
Reading packed data in HEX mode
In ISPF you type HEX ON while browsing a dataset. Each record is shown
as three rows: the characters, then the zone (left) nibble of every
byte, then the numeric (right) nibble underneath. Read each packed
field down, then across. This course's dataset viewer has a HEX
button that does the same. For the record ACC001 with +1234.50:
ACC001..#E.C
444333002404
1330010135C3
Bytes 7–11 read down-and-across as 00 01 23 45 0C: +1234.50. The
character row shows dots and random symbols there, because packed bytes
aren't characters. On z/OS the text columns would be EBCDIC instead
(ACC001 is CCCFFF over 133001), but the packed columns would look
exactly the same.
Record lengths
A record with packed fields is binary data. It must be written to a
fixed-length dataset (ORGANIZATION IS SEQUENTIAL), never a text
file: a packed byte can be X'0D' or X'25', which text tools treat
as line endings.
On the job
"What's in this field?" is a daily question. Browse the dataset in HEX, count bytes from the copybook to the field's offset, and read the nibbles. Tools like File-AID and File Manager will format a record through a copybook for you, but you still need to read raw hex when the copybook is wrong, which is exactly when things break.
Your task
The general ledger feed arrives as text, but the downstream mainframe system expects 12-byte fixed-length records with a packed amount. Write them.
Input, DD name TXNIN, line sequential:
| Field | Picture | Positions |
|---|---|---|
IN-ACCT |
X(6) |
1–6 |
FILLER |
X |
7 |
IN-AMT-TEXT |
X(12), e.g. -1234.50 |
8–19 |
Output, DD name TXNOUT, ORGANIZATION IS SEQUENTIAL,
RECORD CONTAINS 12 CHARACTERS (no line breaks between records):
| Field | Picture | Bytes |
|---|---|---|
OUT-ACCT |
X(6) |
1–6 |
OUT-AMOUNT |
S9(7)V99 COMP-3 |
7–11 |
OUT-TYPE |
X: D if the amount is negative, else C |
12 |
Then display:
RECORDS WRITTEN: 004
Click Run, open the output dataset under Files and press
HEX. Record 2 (ACC002 -1234.56) must read 00012345 6D in bytes
7–11. Find the sign nibble of each record.