The PICTURE Clause

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

The reference material consulted while preparing these pages is listed in the section bibliography.

This page is written against ISO/IEC 1989:2023 (edition 3, January 2023; an edition 4 is upcoming, in DIS ballot), GnuCOBOL 3.2 (28 July 2023, the latest release) and IBM Enterprise COBOL for z/OS 6.5 (PTFs through September 2026), and was checked on 2026-10-20. Every portable example below was compiled with cobc -x -Wall (GnuCOBOL 3.2.0, fixed reference format) and run, and, unless a section says otherwise, also with -std=ibm; the output and the diagnostics shown are real. The ISO standard is paywalled and was not read: the rules for the standard come from the vendor manuals, which say where they follow it.

A PICTURE clause (abbreviated PIC, with an optional IS) describes the shape of an elementary item: which characters or digits each byte may hold, how many bytes there are, where the assumed decimal point sits, whether the value has an operational sign, and how a number is dressed up for printing. It is the one clause that nearly every data description needs. The clause is written on an elementary item only, never on a group; it is forbidden on index items, on RENAMES subjects, on pointers and on the internal floating-point items COMP-1 and COMP-2 (Enterprise COBOL 6.5 Language Reference, PICTURE clause). A picture string is at most 50 characters long in Enterprise COBOL 6.5.

Where the other data-description clauses are covered: Data Division and Level Numbers explains where an item sits in a record and what a group is; Usage and Numeric Representation explains how the digits are stored (zoned decimal, packed decimal, binary) once the picture has fixed their number; VALUE, INITIALIZE and Condition Names covers the VALUE clause in general; REDEFINES, RENAMES and TYPEDEF covers alternative views of the same bytes; and National, UTF-8 and Code Pages covers PIC N and PIC U in depth. This page keeps to the picture string itself and to the clauses (BLANK WHEN ZERO, JUSTIFIED) that act on it.

Reading a picture string

A picture string is a sequence of symbols. A symbol is one character (or the pair CR or DB) with a fixed meaning, and the punctuation characters inside the string are symbols, not separators: the period in ZZ9.99 is a picture symbol, and the period that ends the data description is the one after the string and a space. That is why a picture such as 9.99. is not a typing mistake but an item with a decimal point followed by the entry-ending period.

Three rules cover the notation:

  • Repetition. An unsigned nonzero integer in parentheses after a symbol repeats it, so 9(5)V9(2) and 99999V99 are the same picture. Repetition may be applied to A B G N P X U Z 9 0 / , + - * and the currency symbol.

  • Symbols that occur once. S, V, the period, CR, DB and E (external floating point) may occur only once in a string.

  • Case. Lowercase a b e g n p s u v x z cr db mean the same as their uppercase forms. Other lowercase letters do not, and the currency symbol is case sensitive.

The symbols decide the category of the item, and the category decides everything the rest of the language does with it:

flowchart TD P["PICTURE string"] --> L{"Only A symbols?"} L -->|yes| AL["alphabetic"] L -->|no| N{"Only 9, S, V, P?"} N -->|yes| NU["numeric: arithmetic operand and subscript"] N -->|no| X{"Only X, or A, X and 9 mixed?"} X -->|yes| AN["alphanumeric"] X -->|no| E{"Editing symbols present?"} E -->|"with 9, Z, star, plus, minus, CR, DB, currency"| NE["numeric-edited: for display, not arithmetic"] E -->|"A or X with B, 0 or slash"| AE["alphanumeric-edited"]

Enterprise COBOL 6.5 has eleven categories in all: alphabetic, alphanumeric, alphanumeric-edited, DBCS, external floating-point, national, national-edited, numeric, numeric-edited, UTF-8 and (through USAGE, not PICTURE) internal floating-point. The rules for each are in Data categories and PICTURE rules. This page spends most of its time on four of them, because they are the ones a business program uses every day.

The basic symbols

Six symbols describe data and do no editing. Together they make the alphabetic, alphanumeric and numeric categories.

Symbol Meaning Counted in the size?

9

One decimal digit, 0 to 9.

Yes, one position each (one byte in USAGE DISPLAY).

X

Any character of the computer’s character set.

Yes, one byte.

A

A letter of the Latin alphabet or a space.

Yes, one byte.

S

The item has an operational sign. It says the sign exists, not where it is stored: by default the sign is kept inside the last digit (an overpunch), so no byte is added.

No, unless SIGN IS ... SEPARATE CHARACTER makes it a byte of its own.

V

The assumed decimal point. It is a position in the arithmetic, not a character in storage.

No.

P

An assumed scaling position: a digit that is implied to be zero and not stored. P occurs only as a string of P at the left or at the right end of the digits.

No, but the digit positions count in the number of digits for arithmetic.

Mixing A, X and 9 gives an alphanumeric item that the compiler treats as if the string were all X: a string of only A is alphabetic, a string of only 9 is numeric, and AA99999AAA is alphanumeric, exactly like X(10). The picture then documents what the field should hold; it enforces nothing, and the ALPHABETIC and NUMERIC class conditions are the run-time check.

N, G and U are the double-byte, national and UTF-8 symbols. PIC N is a national (UTF-16) character in Enterprise COBOL when it is used with USAGE NATIONAL (or NSYMBOL(NATIONAL) is in effect) and a DBCS character under NSYMBOL(DBCS); PIC U is a UTF-8 character position and implies USAGE UTF-8; U with BYTE-LENGTH gives a fixed number of bytes with a varying number of characters. They are covered on National, UTF-8 and Code Pages. GnuCOBOL 3.2 treats them differently, as the comparison table near the end of this page shows.

Size, stored bytes and scaling

The size of an item in bytes is the number of positions in the string, with S, V and P not counted (and an optional separate sign counted as one). The example shows the bytes behind five of the Circulation fields, through alphanumeric REDEFINES views, together with the effect of P.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. PICSYM.
      * Basic PICTURE symbols: size, stored bytes, scaling.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-ID               PIC 9(6)      VALUE 123.
       01  WS-ID-X REDEFINES WS-ID           PIC X(6).
       01  WS-NAME             PIC X(12)     VALUE "Ada".
       01  WS-FINE             PIC S9(5)V99  VALUE -12.5.
       01  WS-FINE-X REDEFINES WS-FINE       PIC X(7).
       01  WS-PRICE            PIC 9(4)V99   VALUE 18.99.
       01  WS-PRICE-X REDEFINES WS-PRICE     PIC X(6).
       01  WS-MILLIONS         PIC 9(3)P(6)  VALUE 128000000.
       01  WS-MILLIONS-X REDEFINES WS-MILLIONS PIC X(3).
       01  WS-SMALL            PIC P(3)9(3)  VALUE 0.000123.
       01  WS-SMALL-X REDEFINES WS-SMALL     PIC X(3).
       01  WS-SIZE             PIC 9(2).
       01  WS-WORK             PIC 9(9).
       01  WS-TINY             PIC 9.9(6).
       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
           MOVE FUNCTION LENGTH(WS-ID) TO WS-SIZE
           DISPLAY "9(6)         stored " WS-ID-X " bytes " WS-SIZE
           MOVE FUNCTION LENGTH(WS-NAME) TO WS-SIZE
           DISPLAY "X(12)        stored " WS-NAME " bytes " WS-SIZE
           MOVE FUNCTION LENGTH(WS-FINE) TO WS-SIZE
           DISPLAY "S9(5)V99     stored " WS-FINE-X " bytes " WS-SIZE
           MOVE FUNCTION LENGTH(WS-PRICE) TO WS-SIZE
           DISPLAY "9(4)V99      stored " WS-PRICE-X " bytes " WS-SIZE
           MOVE FUNCTION LENGTH(WS-MILLIONS) TO WS-SIZE
           DISPLAY "9(3)P(6)     stored " WS-MILLIONS-X
                   " bytes " WS-SIZE
           MOVE FUNCTION LENGTH(WS-SMALL) TO WS-SIZE
           DISPLAY "P(3)9(3)     stored " WS-SMALL-X " bytes " WS-SIZE
           MOVE WS-MILLIONS TO WS-WORK
           DISPLAY "9(3)P(6) read as 9(9):     " WS-WORK
           ADD 1000000 TO WS-MILLIONS
           MOVE WS-MILLIONS TO WS-WORK
           DISPLAY "after ADD 1000000:         " WS-WORK
           MOVE WS-SMALL TO WS-TINY
           DISPLAY "P(3)9(3) read as 9.9(6):   " WS-TINY
           STOP RUN.

Compiled with cobc -x -Wall sym.cob (and with -std=ibm), the program prints:

9(6)         stored 000123 bytes 06
X(12)        stored Ada          bytes 12
S9(5)V99     stored 000125p bytes 07
9(4)V99      stored 001899 bytes 06
9(3)P(6)     stored 128 bytes 03
P(3)9(3)     stored 123 bytes 03
9(3)P(6) read as 9(9):     128000000
after ADD 1000000:         129000000
P(3)9(3) read as 9.9(6):   0.000123

What the output shows:

  • S9(5)V99 takes seven bytes, not eight: the S and the V take no room. The last byte is p here and would be } on z/OS, because the sign is an overpunch on the final digit: the digit 0 with a negative sign is the byte p in ASCII and X'D0' (}) in EBCDIC. The sign convention is the subject of the usage page.

  • 9(3)P(6) stores only 128 in three bytes, yet reads as 128000000. ADD 1000000 works on the full value and stores 129. The six P positions are implied zeros: a field like this is how a program keeps a number of millions in three bytes, and the price is that nothing below a million can be stored.

  • P(3)9(3) is the mirror image: the stored 123 means 0.000123, because the implied decimal point is three digit positions to the left of the stored digits.

  • PIC 9(2) reads as the digits 00 to 99, whatever USAGE stores them. The picture defines the decimal range of a binary field as well: a PIC 9(4) COMP item holds 0 to 9999 when TRUNC(STD) is in effect, even though two bytes could hold 65 535.

Digit limits: a numeric item may have 1 to 18 digit positions, or 1 to 31 when ARITH(EXTEND) is in effect in Enterprise COBOL (ARITH(COMPAT) is 18); binary items stay at 18. A numeric-edited item has at most 249 character positions.

In GnuCOBOL, DISPLAY of a numeric item is not editing: with the default dialect it prints the decimal point and sign (0.000123), and with -std=ibm it prints the stored digits (0000123) without them. That is why the example above moves such values to an edited item before printing them.

Numeric-edited items

An item whose string contains any of Z * $ , . + - CR DB B 0 / next to 9 is numeric-edited. The value is still a number: a MOVE into the item aligns it by its decimal point, truncates and zero-fills as for any numeric receiver, and then edits it, replacing leading zeros, inserting punctuation and sign characters. What is stored is text. A numeric-edited item can be displayed, moved to an alphanumeric item or moved back to a numeric one, but it cannot be an arithmetic operand (see the pitfalls).

Symbol Role in a numeric-edited picture

9

A digit position. Always shows its digit, including a zero.

Z

Zero suppression: a leading zero in this position becomes a space.

*

Check protection: a leading zero becomes an asterisk. Z and * cannot both appear in a string.

$ (the currency symbol)

Inserts the currency sign. One $ is a fixed insertion; two or more $ form a floating string. It is the default currency symbol, changed by CURRENCY SIGN.

+ and -

Sign control. + shows + for a positive or zero value and - for a negative one; - shows a space for positive or zero and - for negative. Both may be fixed (first or last character) or floating. Only one of +, -, CR and DB can occur in a string.

CR, DB

Two characters at the right end. Positive or zero gives two spaces; negative gives CR or DB.

,

Inserts a comma. A comma to the left of the first significant digit is replaced by a space or by the fill character.

.

The actual decimal point. It aligns the value (so V is not used with it) and is inserted at that position.

B

Inserts a space.

0

Inserts the digit zero.

/

Inserts a slash.

IBM groups the symbols by what they do (PICTURE clause editing): simple insertion (B 0 / ,), special insertion (the period), fixed insertion ($ + - CR DB, one of each, at an end), floating insertion (a string of $, + or -) and zero suppression and replacement (Z and *).

Zero suppression, check protection and floating insertion

A string of Z, *, $, + or - at the left stands for the leading digit positions. Suppression stops at the first nonzero digit, at the decimal point, or at the first position that is not part of the string (a 9, for example). With Z the zeros become spaces; with * they become asterisks. With a floating $, + or - the zeros become spaces and a single sign or currency character is placed immediately to the left of the first significant digit. The first symbol of a floating string is only ever the sign or currency position, never a digit, which is why a floating string of n symbols can show only n - 1 digits.

The next program formats MEMBER-FINES (PIC S9(5)V99 in the MEMBREC copybook) six ways.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. FINES.
      * Numeric-edited pictures for Circulation fines.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       COPY MEMBREC.
       01  OUT-ZERO-SUPPRESS   PIC ZZ,ZZ9.99.
       01  OUT-ASTERISK        PIC **,**9.99.
       01  OUT-FLOAT-DOLLAR    PIC $$$,$$9.99.
       01  OUT-FIXED-DOLLAR    PIC $ZZ,ZZ9.99.
       01  OUT-FLOAT-MINUS     PIC --,--9.99.
       01  OUT-CREDIT          PIC ZZ,ZZ9.99CR.
       01  OUT-INPUT           PIC -----9.99.
       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
           DISPLAY "FINE       ZZ,ZZ9.99  **,**9.99  $$$,$$9.99"
                   "  $ZZ,ZZ9.99  --,--9.99  ZZ,ZZ9.99CR"
           MOVE 12.50 TO MEMBER-FINES
           PERFORM SHOW-ROW
           MOVE 0 TO MEMBER-FINES
           PERFORM SHOW-ROW
           MOVE 50 TO MEMBER-FINES
           PERFORM SHOW-ROW
           MOVE -3.75 TO MEMBER-FINES
           PERFORM SHOW-ROW
           MOVE 1234.5 TO MEMBER-FINES
           PERFORM SHOW-ROW
           MOVE 0.07 TO MEMBER-FINES
           PERFORM SHOW-ROW
           MOVE 99999.99 TO MEMBER-FINES
           PERFORM SHOW-ROW
           STOP RUN.
       SHOW-ROW.
           MOVE MEMBER-FINES TO OUT-ZERO-SUPPRESS OUT-ASTERISK
                                OUT-FLOAT-DOLLAR OUT-FIXED-DOLLAR
                                OUT-FLOAT-MINUS OUT-CREDIT
           MOVE MEMBER-FINES TO OUT-INPUT
           DISPLAY OUT-INPUT "  [" OUT-ZERO-SUPPRESS "] ["
                   OUT-ASTERISK "] [" OUT-FLOAT-DOLLAR "] ["
                   OUT-FIXED-DOLLAR "] [" OUT-FLOAT-MINUS "] ["
                   OUT-CREDIT "]".

Compiled with cobc -x -Wall fines.cob (and with -std=ibm), the program prints:

FINE       ZZ,ZZ9.99  **,**9.99  $$$,$$9.99  $ZZ,ZZ9.99  --,--9.99  ZZ,ZZ9.99CR
    12.50  [    12.50] [****12.50] [    $12.50] [$    12.50] [    12.50] [    12.50  ]
     0.00  [     0.00] [*****0.00] [     $0.00] [$     0.00] [     0.00] [     0.00  ]
    50.00  [    50.00] [****50.00] [    $50.00] [$    50.00] [    50.00] [    50.00  ]
    -3.75  [     3.75] [*****3.75] [     $3.75] [$     3.75] [    -3.75] [     3.75CR]
  1234.50  [ 1,234.50] [*1,234.50] [ $1,234.50] [$ 1,234.50] [ 1,234.50] [ 1,234.50  ]
     0.07  [     0.07] [*****0.07] [     $0.07] [$     0.07] [     0.07] [     0.07  ]
 99999.99  [99,999.99] [99,999.99] [$99,999.99] [$99,999.99] [ 9,999.99] [99,999.99  ]

The same results as a table (the brackets show where the item starts and ends, since the leading spaces are part of the value):

Fine (MEMBER-FINES) ZZ,ZZ9.99 **,**9.99 $$$,$$9.99

12.50

[    12.50]
[****12.50]
[    $12.50]

0

[     0.00]
[*****0.00]
[     $0.00]

50.00

[    50.00]
[****50.00]
[    $50.00]

-3.75

[     3.75]
[*****3.75]
[     $3.75]

1234.50

[ 1,234.50]
[*1,234.50]
[ $1,234.50]

0.07

[     0.07]
[*****0.07]
[     $0.07]

99999.99

[99,999.99]
[99,999.99]
[$99,999.99]
Fine (MEMBER-FINES) $ZZ,ZZ9.99 --,--9.99 ZZ,ZZ9.99CR

12.50

[$    12.50]
[    12.50]
[    12.50  ]

0

[$     0.00]
[     0.00]
[     0.00  ]

50.00

[$    50.00]
[    50.00]
[    50.00  ]

-3.75

[$     3.75]
[    -3.75]
[     3.75CR]

1234.50

[$ 1,234.50]
[ 1,234.50]
[ 1,234.50  ]

0.07

[$     0.07]
[     0.07]
[     0.07  ]

99999.99

[$99,999.99]
[ 9,999.99]
[99,999.99  ]

Reading the two tables:

  • A Z or an asterisk stays in its position, so the result is always as wide as the picture: ZZ,ZZ9.99 is nine characters whatever the value. The comma is only shown when there is a digit to its left; for 0.07 the comma position holds a space.

  • $$$,$$9.99 and $ZZ,ZZ9.99 look alike and are different. The floating dollar sits next to the first digit ($12.50, $1,234.50), the fixed dollar always stays in column one ($ 12.50). The fixed form is the one that suits a report with a column of amounts that must line up under a heading; the floating form is the cheque-writing form that leaves no room to insert another digit in front.

  • The unsigned pictures lose the sign: -3.75 prints as 3.75 in ZZ,ZZ9.99, * and $ forms. A sign shows only when the picture asks for one (-, +, CR, DB).

  • ZZ,ZZ9.99CR prints CR only for a negative value (a credit to the member’s account) and two spaces otherwise.

  • The last row is the pitfall that the floating minus makes visible: --,--9.99 has nine positions, the first is reserved for the sign, so the value 99999.99 loses its leading 9 and prints 9,999.99. The compiler cannot see it, because the sending item is a variable. See the section on overflow below.

Fixed and floating insertion in detail

  • Fixed insertion uses one currency symbol, +, -, CR or DB, in a fixed position. A $ must be the first character unless it is preceded by + or -. A + or - must be the first or last character; CR and DB must be the two rightmost characters.

  • Floating insertion uses a string of at least two $, or two +, or two -. The leftmost symbol is the leftmost position the sign or currency can reach; the symbols to its right are digit positions that may be suppressed. Simple insertion characters (B 0 / ,) inside or immediately to the right of the string are part of it; a period inside it is also part of it.

  • To avoid truncation, the picture must have a position for every digit of the sender, plus every non-floating insertion character, plus one position for the floating symbol.

  • Replacement. Z, *, +, - and the currency symbol are mutually exclusive as replacement characters in one string. Any of them to the right of the decimal point behaves like a 9.

  • If every digit position is a floating or suppression symbol and the value is zero, then the item is all spaces (Z, floating) or all asterisks except the decimal point (*). ZZZ.ZZ of zero is six spaces, while ZZZ.99 of zero is ` .00` (the 9 after the point keeps its digit).

GnuCOBOL’s Programmer’s Guide adds an extension that is worth knowing because it can hide a portability bug: it accepts several different floating symbols in one picture (+$ZZZZ9.99, +++$$$9.99), which Enterprise COBOL and most other compilers reject, and the manual says the result is bbbb+$1.00 for the value 1 (the b is a space). In the 3.2.0 compiler built for this page the real result of

       IDENTIFICATION DIVISION.
       PROGRAM-ID. MIXED.
      * Two different floating symbols in one picture.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  OUT-MIXED           PIC +$ZZZZ9.99.
       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
           MOVE 1 TO OUT-MIXED
           DISPLAY "[" OUT-MIXED "]"
           STOP RUN.

is

[+$    1.00]

so the sign comes out left of the currency symbol with the spaces after them, which is different from what the guide describes, and the same in every dialect tried (-std=ibm, -std=cobol2014, -std=ibm-strict). Do not rely on either behaviour: use one kind of floating symbol per picture.

Dates, insertion characters and signs

Dates in Circulation are stored as yyyymmdd in a PIC 9(8) field (LOAN-DATE, MEMBER-JOINED). Insertion editing turns them into readable text with / or B; the same symbols and 0 do the same for text. The program also shows the sign forms +, - and DB as fixed insertions, and the de-editing that MOVE performs from an edited sender.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. DATES.
      * Insertion editing: dates, ISBNs, signs and de-editing.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       COPY LOANREC.
       COPY BOOKREC.
       01  OUT-ISO-DATE        PIC 9999/99/99.
       01  OUT-SPACED-DATE     PIC 9999B99B99.
       01  OUT-ZERO-FILL       PIC 9(5)00.
       01  OUT-ISBN-TEXT       PIC XXXBXXXXXXXXXX.
       01  OUT-DATE-TEXT       PIC XXXX/XX/XX.
       01  FINE-EDITED         PIC Z,ZZ9.99.
       01  FINE-NUMBER         PIC 9(5)V99.
       01  FINE-SIGNED         PIC S9(5)V99.
       01  OUT-PLUS-LEAD       PIC +9(5).99.
       01  OUT-MINUS-TRAIL     PIC 9(5).99-.
       01  OUT-NUMBER          PIC 9(5).99.
       01  OUT-DEBIT           PIC 9(5).99DB.
       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
           MOVE 20261020 TO LOAN-DATE
           MOVE LOAN-DATE TO OUT-ISO-DATE OUT-SPACED-DATE
           DISPLAY "9999/99/99    [" OUT-ISO-DATE "]"
           DISPLAY "9999B99B99    [" OUT-SPACED-DATE "]"
           MOVE 1234 TO OUT-ZERO-FILL
           DISPLAY "9(5)00        [" OUT-ZERO-FILL "]"
           MOVE "9780262033848" TO BOOK-ISBN
           MOVE BOOK-ISBN TO OUT-ISBN-TEXT
           DISPLAY "XXXBXXXXXXXXXX [" OUT-ISBN-TEXT "]"
           MOVE "20261020" TO OUT-DATE-TEXT
           DISPLAY "XXXX/XX/XX    [" OUT-DATE-TEXT "]"
           MOVE 12.5 TO OUT-PLUS-LEAD OUT-MINUS-TRAIL OUT-DEBIT
           DISPLAY "+9(5).99      [" OUT-PLUS-LEAD "]"
           DISPLAY "9(5).99-      [" OUT-MINUS-TRAIL "]"
           DISPLAY "9(5).99DB     [" OUT-DEBIT "]"
           MOVE -12.5 TO OUT-PLUS-LEAD OUT-MINUS-TRAIL OUT-DEBIT
           DISPLAY "+9(5).99      [" OUT-PLUS-LEAD "]"
           DISPLAY "9(5).99-      [" OUT-MINUS-TRAIL "]"
           DISPLAY "9(5).99DB     [" OUT-DEBIT "]"
      * De-editing: a numeric-edited sender gives back its value.
           MOVE 1234.5 TO FINE-EDITED
           MOVE FINE-EDITED TO FINE-NUMBER
           MOVE FINE-NUMBER TO OUT-NUMBER
           DISPLAY "de-edited to 9(5)V99:  " OUT-NUMBER
           MOVE -3.75 TO OUT-PLUS-LEAD
           MOVE OUT-PLUS-LEAD TO FINE-SIGNED
           ADD 1 TO FINE-SIGNED
           MOVE FINE-SIGNED TO OUT-PLUS-LEAD
           DISPLAY "de-edit, add 1, edit:  " OUT-PLUS-LEAD
           STOP RUN.

Compiled with cobc -x -Wall dates.cob (and with -std=ibm), the program prints:

9999/99/99    [2026/10/20]
9999B99B99    [2026 10 20]
9(5)00        [0123400]
XXXBXXXXXXXXXX [978 0262033848]
XXXX/XX/XX    [2026/10/20]
+9(5).99      [+00012.50]
9(5).99-      [00012.50 ]
9(5).99DB     [00012.50  ]
+9(5).99      [-00012.50]
9(5).99-      [00012.50-]
9(5).99DB     [00012.50DB]
de-edited to 9(5)V99:  01234.50
de-edit, add 1, edit:  -00002.75

As a table of input, picture and result:

Input Picture Result Item category

LOAN-DATE 20261020

9999/99/99
[2026/10/20]

numeric-edited

LOAN-DATE 20261020

9999B99B99
[2026 10 20]

numeric-edited

PIC 9(5) 1234

9(5)00
[0123400]

numeric-edited

BOOK-ISBN 9780262033848

XXXBXXXXXXXXXX
[978 0262033848]

alphanumeric-edited

PIC X(8) 20261020

XXXX/XX/XX
[2026/10/20]

alphanumeric-edited

12.50

+9(5).99
[+00012.50]

numeric-edited

-12.50

+9(5).99
[-00012.50]

numeric-edited

12.50

9(5).99-
[00012.50 ]

numeric-edited

-12.50

9(5).99-
[00012.50-]

numeric-edited

12.50

9(5).99DB
[00012.50  ]

numeric-edited

-12.50

9(5).99DB
[00012.50DB]

numeric-edited

Notes on the results:

  • 9999/99/99 has ten positions for eight digits: the two slashes are insertions and are counted. The picture does not rearrange digits, so a date stored as yyyymmdd cannot become dd/mm/yyyy with a picture alone; that needs reference modification or a group with three subordinate items (see Dates and Times).

  • In 9(5)00 the two zeros are inserted, so the stored text is the number multiplied by 100 for display. The insertion 0 is not a digit of the value.

  • Pictures such as XXXBXXXXXXXXXX are alphanumeric-edited: they must contain at least one A or X and at least one B, 0 or /. A MOVE of an alphanumeric item into one inserts the editing characters between the characters sent; no arithmetic is involved, and a noninteger numeric sender is not a valid source for such an item.

  • A numeric MOVE into a numeric-edited item aligns on the decimal point first. The edited result of 1234.5 in 9(5).99 is 01234.50: the missing cents are filled with zeros.

De-editing on MOVE

Moving a numeric-edited item into a numeric or numeric-edited receiver de-edits the sender: the compiler works out the value that the edited text represents, sign included, and moves that value. The two last lines of the output above show it: FINE-EDITED holds ` 1,234.50` and the move to PIC 9(5)V99 stores 1234.50; the negative -00003.75 moved to the signed FINE-SIGNED, increased by 1 and edited back gives -00002.75. De-editing is the reason a report line is never the source of an arithmetic result: take the number from the field that was edited, not from the edited copy, whenever the original is still around. The rule is in the Enterprise COBOL MOVE topic (elementary moves).

BLANK WHEN ZERO and JUSTIFIED

Two clauses next to PICTURE change what the picture would do:

  • BLANK WHEN ZERO makes the item all spaces when its value is zero. It is allowed on numeric-edited and numeric items (without S and without *), which must be USAGE DISPLAY or NATIONAL. On a plain numeric item the clause turns it into a numeric-edited item (category changes, so it can no longer be an arithmetic operand). It does not change the value given by VALUE. ZERO, ZEROS and ZEROES are interchangeable.

  • JUSTIFIED RIGHT (abbreviated JUST RIGHT) aligns data at the right end of an alphabetic, alphanumeric, DBCS or national receiving item instead of the left. Data that is too long loses its left-most characters; data that is too short is padded with spaces on the left. It is not allowed on numeric, numeric-edited, alphanumeric-edited, national-edited or floating-point items, nor on UTF-8 items, index items, pointers, RENAMES or condition names.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. CLAUSES.
      * BLANK WHEN ZERO, JUSTIFIED RIGHT and the CURRENCY SIGN clause.
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       SPECIAL-NAMES.
           CURRENCY SIGN IS "K".
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  COPIES-PLAIN        PIC ZZ9.
       01  COPIES-BLANK        PIC 9(3)  BLANK WHEN ZERO.
       01  FINE-BLANK          PIC 9(5).99 BLANK WHEN ZERO.
       01  TITLE-LEFT          PIC X(12).
       01  TITLE-RIGHT         PIC X(12) JUSTIFIED RIGHT.
       01  TITLE-LONG          PIC X(17)
                               VALUE "A very long title".
       01  PRICE-LOCAL         PIC KKKK9.99.
       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
           MOVE 0 TO COPIES-PLAIN COPIES-BLANK FINE-BLANK
           DISPLAY "ZZ9 of 0:               [" COPIES-PLAIN "]"
           DISPLAY "9(3) BLANK WHEN ZERO:   [" COPIES-BLANK "]"
           DISPLAY "9(5).99 BLANK WHEN ZERO [" FINE-BLANK "]"
           MOVE 7 TO COPIES-BLANK
           MOVE 12.5 TO FINE-BLANK
           DISPLAY "9(3) BLANK WHEN ZERO 7: [" COPIES-BLANK "]"
           DISPLAY "9(5).99 BLANK, 12.5:    [" FINE-BLANK "]"
           MOVE "Dune" TO TITLE-LEFT TITLE-RIGHT
           DISPLAY "X(12):                  [" TITLE-LEFT "]"
           DISPLAY "X(12) JUSTIFIED RIGHT:  [" TITLE-RIGHT "]"
           MOVE TITLE-LONG TO TITLE-LEFT TITLE-RIGHT
           DISPLAY "X(12), too long:        [" TITLE-LEFT "]"
           DISPLAY "JUSTIFIED, too long:    [" TITLE-RIGHT "]"
           MOVE 18.99 TO PRICE-LOCAL
           DISPLAY "CURRENCY SIGN K:        [" PRICE-LOCAL "]"
           STOP RUN.

Compiled with cobc -x -Wall clauses.cob (and with -std=ibm), the program prints:

ZZ9 of 0:               [  0]
9(3) BLANK WHEN ZERO:   [   ]
9(5).99 BLANK WHEN ZERO [        ]
9(3) BLANK WHEN ZERO 7: [007]
9(5).99 BLANK, 12.5:    [00012.50]
X(12):                  [Dune        ]
X(12) JUSTIFIED RIGHT:  [        Dune]
X(12), too long:        [A very long ]
JUSTIFIED, too long:    [y long title]
CURRENCY SIGN K:        [  K18.99]

The three zero outputs compare two ways of hiding a zero: ZZ9 prints ` 0` (the last position is a 9, so the zero stays), whereas BLANK WHEN ZERO prints blanks for the whole field, including the decimal point of 9(5).99. JUSTIFIED RIGHT is occasionally used for a title that must hug a right-hand column; moving the 17-character TITLE-LONG into the 12-character items keeps A very long ` in the normal item and `y long title in the justified one: the plain item is truncated on the right, the justified one on the left.

CURRENCY SIGN and DECIMAL-POINT IS COMMA

Two SPECIAL-NAMES entries change what some picture symbols mean:

  • CURRENCY SIGN IS literal chooses the currency symbol used in the pictures (the default is $). In Enterprise COBOL the CURRENCY compiler option is the equivalent, and the clause overrides it. Without PICTURE SYMBOL it is a single character that is not a digit, not a space, not one of the letters A B C D E G N P R S U V X Z (either case) and not one of + - , . * / ; ( ) " = or the apostrophe; it is case sensitive. Enterprise COBOL also allows WITH PICTURE SYMBOL to separate the symbol used in the picture from the currency string that appears in the data (a multi-character sign such as EUR).

  • DECIMAL-POINT IS COMMA exchanges the period and the comma in pictures and in numeric literals. The comma is then the decimal point and the period is the thousands separator.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. COMMA.
      * DECIMAL-POINT IS COMMA swaps the roles of . and , in pictures.
       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       SPECIAL-NAMES.
           DECIMAL-POINT IS COMMA.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  OUT-FINE            PIC ZZ.ZZ9,99.
       01  OUT-PRICE           PIC $$.$$9,99.
       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
           MOVE 1234,5 TO OUT-FINE OUT-PRICE
           DISPLAY "ZZ.ZZ9,99  [" OUT-FINE "]"
           DISPLAY "$$.$$9,99  [" OUT-PRICE "]"
           STOP RUN.

Compiled with cobc -x -Wall comma.cob (and with -std=ibm), the program prints:

ZZ.ZZ9,99  [ 1.234,50]
$$.$$9,99  [$1.234,50]

The CURRENCY SIGN clause is shown in the example of the previous section: CURRENCY SIGN IS "K" followed by PIC KKKK9.99 prints ` K18.99` for 18.99. The symbol cannot be a letter that is already a picture symbol: GnuCOBOL 3.2 rejects "E" with invalid character 'E' in currency symbol. It also reads L as a picture character of its own (a variable-length picture), although the IBM list above does not exclude it, so K was chosen for the example. GnuCOBOL 3.2 does not implement WITH PICTURE SYMBOL: it prints separate currency symbol and currency string is not implemented [-Wpending].

Pitfalls with real diagnostics

Overflow, truncation and lost signs

The picture decides how many digits an item holds, and a MOVE or an arithmetic result that does not fit is truncated on the left (high-order digits) and on the right (low-order digits), without any error. An unsigned receiver loses the sign. The program shows all three and the floating minus that needs one extra position.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. TRUNC.
      * Truncation, lost signs and the room a floating sign needs.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       COPY MEMBREC.
       01  UNSIGNED-FINE       PIC 9(5)V99.
       01  SHORT-FINE          PIC 9(3)V99.
       01  WHOLE-FINE          PIC 9(5).
       01  FLOAT-TIGHT         PIC --,--9.99.
       01  FLOAT-ROOMY         PIC ---,--9.99.
       01  OUT-FINE            PIC -(5)9.99.
       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
           MOVE -1234.56 TO MEMBER-FINES
           MOVE MEMBER-FINES TO UNSIGNED-FINE SHORT-FINE WHOLE-FINE
           MOVE UNSIGNED-FINE TO OUT-FINE
           DISPLAY "to 9(5)V99, sign lost:   " OUT-FINE
           MOVE SHORT-FINE TO OUT-FINE
           DISPLAY "to 9(3)V99, digit lost:  " OUT-FINE
           MOVE WHOLE-FINE TO OUT-FINE
           DISPLAY "to 9(5), cents dropped:  " OUT-FINE
           MOVE 99999.99 TO MEMBER-FINES
           MOVE MEMBER-FINES TO FLOAT-TIGHT FLOAT-ROOMY
           DISPLAY "--,--9.99 of 99999.99:   [" FLOAT-TIGHT "]"
           DISPLAY "---,--9.99 of 99999.99:  [" FLOAT-ROOMY "]"
           STOP RUN.

Compiled with cobc -x -Wall trunc.cob (no warnings, because the values come from variables), the program prints:

to 9(5)V99, sign lost:     1234.56
to 9(3)V99, digit lost:     234.56
to 9(5), cents dropped:    1234.00
--,--9.99 of 99999.99:   [ 9,999.99]
---,--9.99 of 99999.99:  [ 99,999.99]

The rule for sizing an edited picture is the one quoted for floating insertion: positions for every digit, plus each non-floating insertion character, plus one for the floating symbol. ---,--9.99 (ten characters) holds 99,999.99, and --,--9.99 (nine) holds 9,999.99 at most. ON SIZE ERROR protects arithmetic statements, but a plain MOVE truncates silently in both compilers.

Illegal pictures

Each wrong picture below is a separate item, to show every message in one compile (deliberately wrong, so the compiler stops):

       IDENTIFICATION DIVISION.
       PROGRAM-ID. BAD1.
      * Deliberately wrong pictures (one error each).
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  A-SIGN-LAST         PIC 9(5)S.
       01  B-TWO-POINTS        PIC ZZ9.99.99.
       01  C-SIGN-TWICE        PIC +ZZ9.99-.
       01  D-STAR-AND-Z        PIC Z*9.
       01  E-BLANK-STAR        PIC **9 BLANK WHEN ZERO.
       01  F-BAD-LETTER        PIC Q(3).
       01  G-ONE-SYMBOL        PIC +.
       01  H-TWO-V             PIC 9V9V9.
       PROCEDURE DIVISION.
           STOP RUN.

Compiled with cobc -x -Wall bad1.cob, GnuCOBOL 3.2.0 reports (source-context lines left out):

bad1.cob:6: error: S must be at start of PICTURE string
bad1.cob:7: error: . may only occur once in a PICTURE string
bad1.cob:8: error: a trailing +/- sign cannot follow a leading +/- sign
bad1.cob:9: error: cannot have both Z and * in PICTURE string
bad1.cob:11: error: invalid PICTURE character 'Q'
bad1.cob:11: error: PICTURE string must contain at least one of the set A, N, U, X, Z, 1, 9 and *; or at least two of the set +, - and the currency symbol
bad1.cob:12: error: PICTURE string must contain at least one of the set A, N, U, X, Z, 1, 9 and *; or at least two of the set +, - and the currency symbol
bad1.cob:13: error: V may only occur once in a PICTURE string
bad1.cob:10: error: 'E-BLANK-STAR' cannot have * in PICTURE string and BLANK WHEN ZERO

Each message is one rule from the language reference: S only first; the period only once; one sign symbol (+, -, CR, DB) in a string; Z and * mutually exclusive; * not combined with BLANK WHEN ZERO; a string needs at least one of A X Z 9 * or at least two floating symbols; V once.

Edited items are not numbers

A numeric-edited item cannot be an operand of arithmetic, because its content is text. It can be the receiving item of COMPUTE or of ADD ... GIVING, which is how a program normally fills it:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. BAD2.
      * Deliberately wrong: arithmetic on numeric-edited items.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  FINE-EDITED         PIC ZZ,ZZ9.99 VALUE 12.5.
       01  FINE-TOTAL          PIC 9(5)V99.
       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
           ADD 1 TO FINE-EDITED
           COMPUTE FINE-TOTAL = FINE-EDITED * 2
           IF FINE-EDITED > 10
              DISPLAY "big"
           END-IF
           STOP RUN.

Compiled with cobc -x -Wall bad2.cob, GnuCOBOL 3.2.0 prints:

bad2.cob:10: error: 'FINE-EDITED' is not a numeric name
bad2.cob:11: error: 'FINE-EDITED' is not numeric

The usual fix is to keep the number in a numeric item (FINE-TOTAL), do the arithmetic there, and MOVE or COMPUTE the result into the edited item for printing. The IF FINE-EDITED > 10 of the same program compiled without a message, so a comparison is not diagnosed; compare the numeric item instead of relying on the comparison rules for edited items.

COBOL 2023: VALUE for numeric-edited items and the EDITING phrase

Two additions of ISO/IEC 1989:2023 concern pictures (INCITS announcement of the 2023 edition; the text of the standard is paywalled and was not read):

  • VALUE for numeric-edited items: a numeric literal may initialise an edited item, and the literal is edited as a MOVE would edit it.

  • A user-defined EDITING phrase to define editing beyond the fixed symbols.

Enterprise COBOL 6.5 documents neither: in its Numeric-edited items rules, the VALUE of a numeric-edited item has to be an alphanumeric literal or a figurative constant, and the value is assigned without editing. The word EDITING does not appear in the Language Reference.

GnuCOBOL 3.2 accepts the numeric literal VALUE and edits it, with a diagnostic in the IBM dialect, and an error in the 2014 dialect. EDITING is not a reserved word in any dialect of 3.2 (cobc --list-reserved shows it as not reserved), so there is nothing to compile. The example compares the three forms.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. VALED.
      * VALUE on numeric-edited items: numeric literal versus text.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  FINE-NUMERIC-VALUE  PIC ZZ,ZZ9.99 VALUE 1234.5.
       01  FINE-TEXT-VALUE     PIC ZZ,ZZ9.99 VALUE " 1,234.50".
       01  FINE-ZERO-VALUE     PIC ZZ,ZZ9.99 VALUE ZERO.
       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
           DISPLAY "numeric literal: [" FINE-NUMERIC-VALUE "]"
           DISPLAY "text literal:    [" FINE-TEXT-VALUE "]"
           DISPLAY "ZERO:            [" FINE-ZERO-VALUE "]"
           STOP RUN.

Compiled with cobc -x -Wall valed.cob (default dialect), the program prints:

numeric literal: [ 1,234.50]
text literal:    [ 1,234.50]
ZERO:            [000000000]

With -std=ibm the output is the same, with the dialect warning first:

valed.cob:6: warning: numeric literal in VALUE clause of numeric-edited item used [-Wdialect]
    4 |        DATA DIVISION.
    5 |        WORKING-STORAGE SECTION.
    6 >        01  FINE-NUMERIC-VALUE  PIC ZZ,ZZ9.99 VALUE 1234.5.
    7 |        01  FINE-TEXT-VALUE     PIC ZZ,ZZ9.99 VALUE " 1,234.50".
    8 |        01  FINE-ZERO-VALUE     PIC ZZ,ZZ9.99 VALUE ZERO.
numeric literal: [ 1,234.50]
text literal:    [ 1,234.50]
ZERO:            [000000000]

With -std=cobol2014 the numeric literal is refused:

valed.cob:6: error: numeric literal in VALUE clause of numeric-edited item used
    4 |        DATA DIVISION.
    5 |        WORKING-STORAGE SECTION.
    6 >        01  FINE-NUMERIC-VALUE  PIC ZZ,ZZ9.99 VALUE 1234.5.
    7 |        01  FINE-TEXT-VALUE     PIC ZZ,ZZ9.99 VALUE " 1,234.50".
    8 |        01  FINE-ZERO-VALUE     PIC ZZ,ZZ9.99 VALUE ZERO.

The ZERO line deserves a second look: the figurative constant fills the item with the character 0 and does no editing, so a numeric-edited item initialised with VALUE ZERO shows 000000000, not ` 0.00` or blanks. If you want a blank or a formatted zero on the first DISPLAY, MOVE the number in (INITIALIZE also gives ` 0.00` in GnuCOBOL 3.2) or give an alphanumeric literal of the exact edited text, as the second line does.

Support in GnuCOBOL 3.2 versus Enterprise COBOL 6.5

Feature Enterprise COBOL 6.5 GnuCOBOL 3.2

Digits in a numeric item

18, or 31 with ARITH(EXTEND)

Not checked on this page.

Several different floating symbols in one picture

Not documented as allowed (the Programmer’s Guide says most other implementations reject it)

Accepted (extension); result differs from the guide.

PIC N

National character with USAGE NATIONAL (UTF-16) or DBCS under NSYMBOL(DBCS)

N(10) is 10 bytes alone and 20 bytes with USAGE NATIONAL; both are flagged -Wunfinished.

PIC U

UTF-8 character position, USAGE UTF-8

U(10) is accepted with no diagnostic and reserves 10 bytes.

VALUE of a numeric literal on a numeric-edited item

Not allowed (alphanumeric literal, no editing)

Accepted, edited; warning in -std=ibm, error in -std=cobol2014.

EDITING phrase (2023)

Not in the Language Reference

Not reserved, not supported.

CURRENCY SIGN ... WITH PICTURE SYMBOL

Supported

Not implemented (-Wpending warning).

Where the book is outdated

The book consulted for this section (Taulli, Modern Mainframe Development, 2022) introduces PIC and editing in its chapter on data, and nothing is copied from it. Three readings need care today:

  • The REDEFINES example for an edited price. The chapter’s example redefines an edited price with an alphanumeric item, but the REDEFINES entry has a stray PIC keyword before its picture, which is not valid COBOL, and the X(6) size does not match the edited item it redefines, which has seven characters. The correct form is the PIC X(7) view shown in the example below: a view of the same bytes must cover every character of the edited item or the last one is invisible through it.

  • Editing is for display, not for arithmetic. The same chapter can be read as if edited items could be added up. They cannot, as the diagnostics above show: keep the number in a numeric item and edit a copy.

  • USAGE is not part of the picture. The picture gives the decimal digits and the editing, and USAGE (DISPLAY, COMP-3, COMP) decides the bytes. Edited items must be DISPLAY (or NATIONAL) and cannot be COMP-3 or binary. For the related claim that binary items are "not good for math because of rounding", see the usage page: binary integers are exact, and the real issues are truncation and scaling.

The corrected view of an edited price, with the whole item and a deliberately short view:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. REDEF.
      * An alphanumeric view of an edited price must be as long as it.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  BOOK-PRICE-EDITED   PIC $ZZ9.99.
       01  BOOK-PRICE-TEXT REDEFINES BOOK-PRICE-EDITED PIC X(7).
       01  BOOK-PRICE-SHORT REDEFINES BOOK-PRICE-EDITED PIC X(6).
       PROCEDURE DIVISION.
       MAIN-PARAGRAPH.
           MOVE 18.99 TO BOOK-PRICE-EDITED
           DISPLAY "whole item:  [" BOOK-PRICE-TEXT "]"
           DISPLAY "six bytes:   [" BOOK-PRICE-SHORT "]"
           STOP RUN.
whole item:  [$ 18.99]
six bytes:   [$ 18.9]

Nothing in the compiler flags the shorter view: for a non-EXTERNAL item, REDEFINES imposes no size constraint, so the mismatch is a defect only a reader or a test can see.