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)and99999V99are the same picture. Repetition may be applied toA B G N P X U Z 9 0 / , + - *and the currency symbol. -
Symbols that occur once.
S,V, the period,CR,DBandE(external floating point) may occur only once in a string. -
Case. Lowercase
a b e g n p s u v x z cr dbmean 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:
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? |
|---|---|---|
|
One decimal digit, |
Yes, one position each (one byte in |
|
Any character of the computer’s character set. |
Yes, one byte. |
|
A letter of the Latin alphabet or a space. |
Yes, one byte. |
|
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 |
|
The assumed decimal point. It is a position in the arithmetic, not a character in storage. |
No. |
|
An assumed scaling position: a digit that is implied to be zero and not stored. |
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
Source: Enterprise COBOL 6.5 Language Reference: Symbols used in the PICTURE clause and GnuCOBOL Programmer’s Guide: PICTURE
What the output shows:
-
S9(5)V99takes seven bytes, not eight: theSand theVtake no room. The last byte isphere and would be}on z/OS, because the sign is an overpunch on the final digit: the digit0with a negative sign is the bytepin ASCII andX'D0'(}) in EBCDIC. The sign convention is the subject of the usage page. -
9(3)P(6)stores only128in three bytes, yet reads as128000000.ADD 1000000works on the full value and stores129. The sixPpositions 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 stored123means0.000123, because the implied decimal point is three digit positions to the left of the stored digits. -
PIC 9(2)reads as the digits00to99, whateverUSAGEstores them. The picture defines the decimal range of a binary field as well: aPIC 9(4) COMPitem holds 0 to 9999 whenTRUNC(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 |
|---|---|
|
A digit position. Always shows its digit, including a zero. |
|
Zero suppression: a leading zero in this position becomes a space. |
|
Check protection: a leading zero becomes an asterisk. |
|
Inserts the currency sign. One |
|
Sign control. |
|
Two characters at the right end. Positive or zero gives two spaces; negative gives |
|
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 |
|
Inserts a space. |
|
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 ]
Source: Enterprise COBOL 6.5 Language Reference: Zero suppression and replacement editing, Floating insertion editing and Fixed insertion editing
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
Zor an asterisk stays in its position, so the result is always as wide as the picture:ZZ,ZZ9.99is nine characters whatever the value. The comma is only shown when there is a digit to its left; for0.07the comma position holds a space. -
$$$,$$9.99and$ZZ,ZZ9.99look 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.75inZZ,ZZ9.99,*and$forms. A sign shows only when the picture asks for one (-,+,CR,DB). -
ZZ,ZZ9.99CRprintsCRonly 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.99has nine positions, the first is reserved for the sign, so the value 99999.99 loses its leading9and prints9,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,
+,-,CRorDB, 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;CRandDBmust 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 a9. -
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.ZZof zero is six spaces, whileZZZ.99of zero is ` .00` (the9after 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
Source: Enterprise COBOL 6.5 Language Reference: Simple insertion editing and Special insertion editing
As a table of input, picture and result:
| Input | Picture | Result | Item category |
|---|---|---|---|
|
9999/99/99 |
[2026/10/20] |
numeric-edited |
|
9999B99B99 |
[2026 10 20] |
numeric-edited |
|
9(5)00 |
[0123400] |
numeric-edited |
|
XXXBXXXXXXXXXX |
[978 0262033848] |
alphanumeric-edited |
|
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/99has ten positions for eight digits: the two slashes are insertions and are counted. The picture does not rearrange digits, so a date stored asyyyymmddcannot becomedd/mm/yyyywith a picture alone; that needs reference modification or a group with three subordinate items (see Dates and Times). -
In
9(5)00the two zeros are inserted, so the stored text is the number multiplied by 100 for display. The insertion0is not a digit of the value. -
Pictures such as
XXXBXXXXXXXXXXare alphanumeric-edited: they must contain at least oneAorXand at least oneB,0or/. AMOVEof 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
MOVEinto a numeric-edited item aligns on the decimal point first. The edited result of1234.5in9(5).99is01234.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 ZEROmakes the item all spaces when its value is zero. It is allowed on numeric-edited and numeric items (withoutSand without*), which must beUSAGE DISPLAYorNATIONAL. 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 byVALUE.ZERO,ZEROSandZEROESare interchangeable. -
JUSTIFIED RIGHT(abbreviatedJUST 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,RENAMESor 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]
Source: Enterprise COBOL 6.5 Language Reference: BLANK WHEN ZERO clause, JUSTIFIED clause and GnuCOBOL Programmer’s Guide: BLANK WHEN ZERO
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 literalchooses the currency symbol used in the pictures (the default is$). In Enterprise COBOL theCURRENCYcompiler option is the equivalent, and the clause overrides it. WithoutPICTURE SYMBOLit is a single character that is not a digit, not a space, not one of the lettersA 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 allowsWITH PICTURE SYMBOLto separate the symbol used in the picture from the currency string that appears in the data (a multi-character sign such asEUR). -
DECIMAL-POINT IS COMMAexchanges 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):
-
VALUEfor numeric-edited items: a numeric literal may initialise an edited item, and the literal is edited as aMOVEwould edit it. -
A user-defined
EDITINGphrase 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 |
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. |
|
National character with |
|
|
UTF-8 character position, |
|
|
Not allowed (alphanumeric literal, no editing) |
Accepted, edited; warning in |
|
Not in the Language Reference |
Not reserved, not supported. |
|
Supported |
Not implemented ( |
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
REDEFINESexample for an edited price. The chapter’s example redefines an edited price with an alphanumeric item, but theREDEFINESentry has a strayPICkeyword before its picture, which is not valid COBOL, and theX(6)size does not match the edited item it redefines, which has seven characters. The correct form is thePIC 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.
-
USAGEis not part of the picture. The picture gives the decimal digits and the editing, andUSAGE(DISPLAY,COMP-3,COMP) decides the bytes. Edited items must beDISPLAY(orNATIONAL) and cannot beCOMP-3or 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.
References
-
Enterprise COBOL for z/OS 6.5 Language Reference: PICTURE clause
-
Enterprise COBOL for z/OS 6.5 Language Reference: Symbols used in the PICTURE clause
-
Enterprise COBOL for z/OS 6.5 Language Reference: Character-string representation
-
Enterprise COBOL for z/OS 6.5 Language Reference: Data categories and PICTURE rules
-
Enterprise COBOL for z/OS 6.5 Language Reference: PICTURE clause editing
-
Enterprise COBOL for z/OS 6.5 Language Reference: BLANK WHEN ZERO clause
-
Enterprise COBOL for z/OS 6.5 Language Reference: JUSTIFIED clause
-
Enterprise COBOL for z/OS 6.5 Language Reference: VALUE clause
-
Enterprise COBOL for z/OS 6.5 Language Reference: REDEFINES clause
-
Enterprise COBOL for z/OS 6.5 Language Reference: CURRENCY SIGN clause
-
Enterprise COBOL for z/OS 6.5 Language Reference: DECIMAL-POINT IS COMMA clause
-
Enterprise COBOL for z/OS 6.5 Language Reference: Elementary moves
-
Enterprise COBOL for z/OS 6.5 Language Reference: Alignment rules
-
INCITS: Available now — 2023 edition of ISO/IEC 1989 (COBOL)
-
Modern Mainframe Development (Tom Taulli, O’Reilly Media, 2022), consulted as a narrative