PICTURE clauses
The PIC (short for PICTURE) clause is a template: one symbol per
character position, describing what the field can hold. COBOL fields are
fixed length, so the picture also fixes the size.
| Symbol | Means | Example |
|---|---|---|
X |
any character | PIC X(20): 20 characters of text |
9 |
a decimal digit | PIC 9(5): a 5-digit number |
A |
a letter or space | PIC A(2): two letters |
V |
implied decimal point | PIC 9(3)V99: 3 digits, then 2 decimals |
S |
the value can be negative | PIC S9(5)V99 |
A number in brackets repeats the symbol: 9(5) is shorthand for 99999,
and 9(3)V99 is 999V99.
IDENTIFICATION DIVISION.
PROGRAM-ID. PICS.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-PRICE PIC 9(3)V99 VALUE 12.5.
01 WS-BALANCE PIC S9(5)V99 VALUE -250.75.
01 WS-CODE PIC X(4) VALUE "A1-Z".
01 WS-INITIALS PIC A(2) VALUE "JM".
PROCEDURE DIVISION.
DISPLAY "PRICE: " WS-PRICE.
DISPLAY "BALANCE: " WS-BALANCE.
DISPLAY "CODE: " WS-CODE.
DISPLAY "INITIALS: " WS-INITIALS.
DISPLAY "BYTES IN PRICE: " FUNCTION LENGTH(WS-PRICE).
DISPLAY "BYTES IN BALANCE: " FUNCTION LENGTH(WS-BALANCE).
STOP RUN.
PRICE: 012.50
BALANCE: -00250.75
CODE: A1-Z
INITIALS: JM
BYTES IN PRICE: 5
BYTES IN BALANCE: 7
V: the decimal point that isn't there
V tells the compiler where the decimal point would be, but no point
is stored. WS-PRICE is five bytes holding the digits 01250; the
compiler knows the last two are decimals. That is why FUNCTION LENGTH
reports 5, not 6.
This is exactly how money arrives in mainframe files: a field such as
0012550 in a record, described by PIC 9(5)V99, means 125.50. Get the
picture wrong (say PIC 9(7)) and your program reads it as 12,550, a
hundred times too big.
S: the sign
Without S, a numeric field can't be negative. Move a negative value into
it and the sign is silently dropped:
IDENTIFICATION DIVISION.
PROGRAM-ID. SIGNS.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-REFUND PIC S9(3) VALUE -42.
01 WS-NO-SIGN PIC 9(3).
PROCEDURE DIVISION.
MOVE WS-REFUND TO WS-NO-SIGN.
DISPLAY "SIGNED: " WS-REFUND.
DISPLAY "UNSIGNED: " WS-NO-SIGN.
STOP RUN.
SIGNED: -042
UNSIGNED: 042
A refund of -42 just became a charge of 42. Any field that can go negative
(balances, adjustments, differences) needs S.
Like V, the S takes no extra byte: the sign is stored inside the last
digit (this is called an overpunched sign, another punched-card legacy).
GnuCOBOL versus z/OS DISPLAY
GnuCOBOL's DISPLAY helpfully shows 012.50 and -00250.75. On z/OS,
DISPLAY writes the raw digits: 01250, and 002507N for the
negative balance, where N is the last digit 5 with the sign
overpunched on it. For readable output on any platform, use the edited
pictures you'll meet later in this module.
X versus 9 versus A
Use 9 only for values you do arithmetic on. Account numbers, phone
numbers and postcodes are identifiers, not quantities: declare them X,
so leading zeros and letters survive. A is rare in practice; most shops
use X for all text.
On the job
When a program produces totals that are exactly 100 times too big or
too small, check the V positions in the record layout first.
Your task
A savings account record arrives as one line of 19 characters with no decimal points, exactly as it would sit in a mainframe file:
AC1002S001255000350
The spec for the record is:
| Positions | Field | Content |
|---|---|---|
| 1–6 | IN-ACCT |
account number, text |
| 7 | IN-TYPE |
account type, one letter |
| 8–14 | IN-BAL |
balance: 5 whole digits, 2 decimals |
| 15–19 | IN-RATE |
interest rate: 1 whole digit, 4 decimals |
The program reads the whole record into the group IN-REC with one
ACCEPT. Fix the PIC clauses so the fields match the spec. The output
for the record above must be:
ACCOUNT: AC1002
TYPE: S
BALANCE: 00125.50
RATE: 0.0350
Don't change the PROCEDURE DIVISION, and keep each field the same
length so the positions still line up.