COBOL Textbook

COBOL PICTURE clause: 9, X, A, V, S and how a number is stored

What each PICTURE symbol means, how PIC 9(3)V99 occupies five bytes with no decimal point, and what S and P really do, shown by one GnuCOBOL program.

Published
Updated
Dialect
GnuCOBOL 3.x, free format
Status
Compiled and run with GnuCOBOL 3.2
Length
1,311 words, 6 min

A PICTURE clause is the closest thing COBOL has to a type declaration, and it is also a memory layout. This lesson takes the seven symbols you will meet most (9, X, A, V, S, P and the repeat count) and shows, with one program, what each does to the bytes in storage and to what DISPLAY prints. If you came from a language where int and string hide their representation, this is the lesson where COBOL stops hiding.

The program is desk-checked against the GnuCOBOL 3.2 Programmer's Guide (section 6.9.33 for PICTURE, 6.9.49 for USAGE) and against IBM's symbol table for Enterprise COBOL. The expected output was first derived by hand from those two documents, and the derivation is written out below so you can check it. On 29 October 2026 we compiled and ran the program with GnuCOBOL 3.2, which showed that the P line of that derivation was wrong; the section on WS-Scaled below has the correction.

The program

       >>SOURCE FORMAT IS FREE
*> cobol-picture-clause-9-x-a-v-s.cob - PICTURE symbols 9, X, A, V, S and P,
*>   and what a numeric item looks like in storage
*> Dialect: GnuCOBOL 3.x, free format
*> Status : desk-checked against the GnuCOBOL 3.2 Programmer's Guide;
*>          compiled and run with GnuCOBOL 3.2 on 2026-10-29
*> Inputs : none
*> Outputs: nine lines on standard output
*> Build  : cobc -x cobol-picture-clause-9-x-a-v-s.cob
*> Run    : ./cobol-picture-clause-9-x-a-v-s
*>          (Windows: cobol-picture-clause-9-x-a-v-s.exe)

IDENTIFICATION DIVISION.
PROGRAM-ID. PICTURE-DEMO.

ENVIRONMENT DIVISION.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-Unsigned-Int    PIC 9(5).
01 WS-Signed-Int      PIC S9(5).
01 WS-Unsigned-Dec    PIC 9(3)V99.
01 WS-Signed-Dec      PIC S9(3)V99.
01 WS-Alnum           PIC X(10).
01 WS-Alpha           PIC A(5).
01 WS-Scaled          PIC 9(3)P(3).

*> A numeric item and an alphanumeric view of the same five bytes.
01 WS-Raw-Holder.
   05 WS-Raw-Number   PIC 9(3)V99 VALUE 123.45.
01 WS-Raw-Bytes REDEFINES WS-Raw-Holder PIC X(5).

PROCEDURE DIVISION.
MAIN-PARAGRAPH.
    MOVE 42        TO WS-Unsigned-Int
    MOVE -42       TO WS-Signed-Int
    MOVE 3.14159   TO WS-Unsigned-Dec
    MOVE -0.5      TO WS-Signed-Dec
    MOVE "COBOL"   TO WS-Alnum
    MOVE "ABC"     TO WS-Alpha
    MOVE 128000    TO WS-Scaled

    DISPLAY "PIC 9(5)     : " WS-Unsigned-Int
    DISPLAY "PIC S9(5)    : " WS-Signed-Int
    DISPLAY "PIC 9(3)V99  : " WS-Unsigned-Dec
    DISPLAY "PIC S9(3)V99 : " WS-Signed-Dec
    DISPLAY "PIC X(10)    : [" WS-Alnum "]"
    DISPLAY "PIC A(5)     : [" WS-Alpha "]"
    DISPLAY "PIC 9(3)P(3) : " WS-Scaled
    DISPLAY "9(3)V99 holding 123.45, as numeric : " WS-Raw-Number
    DISPLAY "9(3)V99 holding 123.45, raw bytes  : [" WS-Raw-Bytes "]"
    STOP RUN.
Listing 1.2-1. Lines 1 to 52 of cobol-picture-clause-9-x-a-v-s.cob.

The symbols

Each symbol in a picture string stands for one character position in storage, with two exceptions, V and P, which stand for nothing at all. A count in parentheses repeats the symbol before it, so 9(5) is the same as 99999. The IBM reference caps a picture string at 50 characters, which is why the repeat count exists.

Table 1.2-1
Symbol Meaning (Programmer's Guide 6.9.33) Storage Example
9 one decimal digit, 0 to 9 one character PIC 9(5) holds 00000 to 99999
X any character one character PIC X(10)
A a letter or a space one character PIC A(5)
V position of the assumed decimal point none PIC 9(3)V99 is five bytes
S the item is signed; must be first none extra by default PIC S9(5)
P an assumed zero digit at either end none PIC 9(3)P(3) holds 0 to 999,000 in steps of 1,000, in 3 bytes

The table above is the whole story for what the Programmer's Guide calls the basic data types. An item whose picture has only 9s (plus S, V and P) is numeric; only As makes it alphabetic; anything else, including any mix, is alphanumeric. The classification matters because arithmetic verbs accept only numeric items, and because the NUMERIC class test, which the REDEFINES lesson applies before doing arithmetic on text, checks whether the bytes in an item are all digits.

Line by line

WS-Unsigned-Int and WS-Signed-Int are both five digits. The difference is the S. Without it, the Programmer's Guide says, any negative value moved into the item "will have the negative sign stripped from it", and what remains is the absolute value. With it, the item can hold a sign. Where does the sign live, given that S adds no storage? By default it is embedded in the last digit position, sharing the byte with the digit. Exactly which byte values encode a negative digit is not something the Programmer's Guide specifies, and it differs between EBCDIC mainframes and ASCII builds, so this lesson makes no claim about it. If you need the sign as a visible separate character in the record, the SIGN IS LEADING SEPARATE clause exists for that; it does add one byte.

WS-Unsigned-Dec is PIC 9(3)V99. The V marks where the decimal point is assumed to be, but no point is stored. The item is five bytes: three integer digits and two fractional digits. When 3.14159 is moved in, the point in the literal is aligned with the V, the digits are copied outward from it, and anything that does not fit is dropped: the receiver ends up holding 00314, meaning 003.14. No rounding happens on a MOVE; the MOVE lesson has a table of exactly these cases.

WS-Alnum and WS-Alpha show padding. A five-character literal moved into a ten-character X item fills from the left and the remaining five positions become spaces. The A picture accepts only letters and spaces, but the guide is clear that this is documentation more than enforcement: A(2)9(5)A(3) and X(10) allocate the same ten bytes, and neither checks anything at run time by itself.

WS-Scaled uses P, the symbol nobody remembers. PIC 9(3)P(3) allocates three bytes but is treated in every MOVE and calculation as if three zeros followed the stored digits. Moving 128000 into it stores 128, and the item's value is 128 thousand: moved on into a PIC 9(9) item, it arrives as 000128000. DISPLAY writes the value out with its scaling zeros, 128000, six characters for a three-byte item. The limit is the part to remember. Three stored digits followed by three assumed zeros reach 999,000 at most, so the picture cannot hold millions. An earlier version of this program moved 128000000 into the item and said it held 128 million; it does not fit, the high-order digits are dropped as in any other numeric move, and a real run displays 000000.

The last two entries are the reason this program exists. WS-Raw-Holder is a group item whose only field is PIC 9(3)V99 with a starting value of 123.45. WS-Raw-Bytes REDEFINES the group as five plain characters, so it lets the program display the same memory without any numeric interpretation. The Programmer's Guide's USAGE table says a USAGE DISPLAY numeric item stores one character per 9, and nothing for V. So the raw view must be the five characters 12345: no point, no spacing, just digits. Every fixed-width file that a COBOL program writes looks like this inside, which is why a .dat file full of prices is unreadable until you know the copybook.

Compile and run

cobc -x cobol-picture-clause-9-x-a-v-s.cob
./cobol-picture-clause-9-x-a-v-s

Nothing else is needed; the >>SOURCE FORMAT IS FREE directive on line 1 covers the free-format source, and the first program lesson explains why it sits at column 8.

Worked example

Output of the compiled program (GnuCOBOL 3.2, 29 October 2026):

PIC 9(5)     : 00042
PIC S9(5)    : -00042
PIC 9(3)V99  : 003.14
PIC S9(3)V99 : -000.50
PIC X(10)    : [COBOL     ]
PIC A(5)     : [ABC  ]
PIC 9(3)P(3) : 128000
9(3)V99 holding 123.45, as numeric : 123.45
9(3)V99 holding 123.45, raw bytes  : [12345]

Two renderings depend on GnuCOBOL's default pretty-display configuration, which formats numeric items for reading: it shows a separate leading sign on signed items, and it inserts the decimal point at the V position, so 00314 is shown as 003.14. Without that option, or on a mainframe, the same items would display as their raw bytes. The final line is the one that does not depend on any option: five digits, because five digits is what is there.

Limitations

This lesson covers the basic symbols and deliberately stops before the editing symbols (Z, ,, ., CR, DB, -, +, $, *), which turn a numeric item into a numeric-edited one that can no longer be used in arithmetic; those get their own lesson. It also stays with USAGE DISPLAY, the default. The same pictures with USAGE BINARY, COMP-3 (packed decimal, an IBM usage that GnuCOBOL supports) or COMP-5 store the value in fewer bytes and in a form the raw-bytes trick would not print legibly. The program was run on GnuCOBOL 3.2 only, and how DISPLAY writes out a P item is a place where other compilers may differ. And the encoding of the embedded sign is intentionally left unstated, because the documentation does not fix it and the two platforms this site cares about do it differently.

Correction, 29 October 2026: this lesson said PIC 9(3)P(3) "holds millions in 3 bytes" and showed 128000000 moved into it displaying as 000000128. Both were wrong: the picture holds at most 999,000, and a real GnuCOBOL 3.2 run of that move displays 000000, because the high-order digits do not fit. The program now moves 128000, which fits and displays as 128000, and the table row and the explanation are corrected. With that line fixed, the output shown matches a real run line for line, so the page now says the program was compiled and run.

Index terms cobol, gnucobol, picture clause, data division, usage display