USAGE and Numeric Representation

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, without a warning except in the ones labelled deliberately wrong; the output shown is real, and where the text says that an example was also run with -std=ibm or another option, the differences are shown. The programs ran on a little-endian arm64 host, which matters only for COMP-5 and the floating-point bytes, and those lines say so. The ISO standard is paywalled and was not read: the rules come from the vendor manuals. The IBM statements come from the Enterprise COBOL documentation and were not run here (z/OS; not run here).

A number in COBOL is more than a value: it is a value and a storage format. The PICTURE clause says how many digits the item has and where the decimal point and the sign are (PICTURE Clause); the USAGE clause says how those digits are laid out in bytes. The same value +1234.5 in PIC S9(5)V9 can occupy six bytes of characters, four bytes of packed nibbles or four bytes of a binary integer, and which one is used decides the file record size, how fast arithmetic is, how the field must be treated when it crosses to another language or another machine, and what a hex dump of it looks like. This page covers every usage the compilers support, the exact bytes of each, the compiler options that change how binary and decimal data are processed (TRUNC, NUMPROC, GnuCOBOL’s binary-size and binary-truncate), and how to look at the bytes. The surrounding data description is in Data Division and Level Numbers, the arithmetic that uses these items in Arithmetic and COMPUTE, and the character-set side of NATIONAL and UTF-8 in National UTF-8 and Code Pages.

The USAGE clause

USAGE is an optional clause of a data description entry, written as USAGE IS COMP-3 or just COMP-3 (the words USAGE and IS are optional). It may be given on any level number except 66 and 88. On a group item it applies to every elementary item in the group, and an elementary item may not contradict the group’s usage. When no usage is written, it is implied from the PICTURE: DISPLAY when the picture has no G, N or U; NATIONAL for an N picture only when the NSYMBOL(NATIONAL) option is in effect, DISPLAY-1 (DBCS) when NSYMBOL(DBCS) is; UTF-8 for a U picture (Enterprise COBOL 6.5).

A computational item is one meant for arithmetic: it must be numeric, and its picture may contain only 9, S, V and P. Enterprise COBOL limits it to 18 decimal digits, except PACKED-DECIMAL, which holds 18 digits under ARITH(COMPAT) (the default) and 31 under ARITH(EXTEND). GnuCOBOL accepts up to 38 digits in any numeric field, but a binary item is limited to 18 digits and cobc says so. This program is deliberately wrong: a 19-digit binary item and a 39-digit packed item (the first message is the 38-digit limit, the second the binary one):

       IDENTIFICATION DIVISION.
       PROGRAM-ID. WIDE.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  W-1  PIC 9(31) COMP-3.
       01  W-2  PIC 9(38) COMP-3.
       01  W-3  PIC 9(19) COMP.
       01  W-4  PIC 9(38).
       01  W-5  PIC 9(39) COMP-3.
       PROCEDURE DIVISION.
           DISPLAY FUNCTION LENGTH(W-1) " " FUNCTION LENGTH(W-2) " "
               FUNCTION LENGTH(W-3) " " FUNCTION LENGTH(W-4)
           STOP RUN.
wide.cob:9: error: numeric field cannot be larger than 38 digits
    7 |        01  W-3  PIC 9(19) COMP.
    8 |        01  W-4  PIC 9(38).
    9 >        01  W-5  PIC 9(39) COMP-3.
   10 |        PROCEDURE DIVISION.
   11 |            DISPLAY FUNCTION LENGTH(W-1) " " FUNCTION LENGTH(W-2) " "
wide.cob:7: error: 'W-3' binary field cannot be larger than 18 digits
    5 |        01  W-1  PIC 9(31) COMP-3.
    6 |        01  W-2  PIC 9(38) COMP-3.
    7 >        01  W-3  PIC 9(19) COMP.
    8 |        01  W-4  PIC 9(38).
    9 |        01  W-5  PIC 9(39) COMP-3.

The usages at a glance

The table lists every usage with its storage. "z/OS" means Enterprise COBOL 6.5 (from its Language Reference and Programming Guide); "GnuCOBOL" means 3.2.0 as measured by the programs on this page and described in its Programmer’s Guide.

Usage Meaning and picture Bytes Notes

DISPLAY

zoned decimal, or characters; the default

one per picture position (SIGN SEPARATE adds one)

the sign is overpunched in the zone of one digit byte unless SIGN IS SEPARATE

PACKED-DECIMAL, COMP-3

packed decimal, two digits per byte, sign in the last nibble

digits plus one, divided by two, rounded up

the same in both compilers; an odd digit count wastes no half byte

BINARY, COMP, COMP-4

binary integer, scaled by the picture

1 to 4 digits: 2, 5 to 9: 4, 10 to 18: 8

big-endian on z/OS; big-endian in GnuCOBOL by default; TRUNC or binary-truncate limits the value to the picture

COMP-5

native binary, scaled by the picture

2, 4 or 8 on z/OS; GnuCOBOL also uses 1 for one or two digits (see below)

holds the full range of the bytes; native byte order of the machine

COMP-X, COMP-6, BINARY-CHAR, BINARY-SHORT, BINARY-LONG, BINARY-DOUBLE

GnuCOBOL extensions (COMP-6 is unsigned packed decimal)

1, 2, 4, 8 and so on

not in Enterprise COBOL 6.5; BINARY-CHAR and the like are listed there only as potential reserved words

COMP-1, FLOAT-SHORT

single-precision floating point; no picture

4

hexadecimal floating point on z/OS (COMP-1 only); IEEE 754 binary32 in GnuCOBOL

COMP-2, FLOAT-LONG

double-precision floating point; no picture

8

hexadecimal floating point on z/OS (COMP-2 only); IEEE 754 binary64 in GnuCOBOL

FLOAT-DECIMAL-16, FLOAT-DECIMAL-34

IEEE 754 decimal floating point (COBOL 2014); no picture

8 and 16

GnuCOBOL only; not in the Enterprise COBOL 6.5 USAGE clause

INDEX

index data item, holds the value of a table index

4 in GnuCOBOL

used with SET and table subscripts

POINTER, POINTER-32

address of a data item

4 or 8 on z/OS depending on LP(32) or LP(64); POINTER-32 always 4; 8 in 64-bit GnuCOBOL

POINTER-32 is Enterprise COBOL 6.3 and later and is not accepted by GnuCOBOL 3.2

PROCEDURE-POINTER, FUNCTION-POINTER

address of an entry point

as for POINTER

GnuCOBOL spells the procedure pointer PROGRAM-POINTER

NATIONAL

UTF-16 characters (CCSID 1200), two bytes each

two per position

GnuCOBOL 3.2 accepts the word but warns that it is unfinished

UTF-8

UTF-8 characters (CCSID 1208), PIC U

one to four per character

Enterprise COBOL 6.3 and later; GnuCOBOL 3.2 warns that it is unfinished

DISPLAY-1

DBCS characters, two bytes each

two per position

Enterprise COBOL; GnuCOBOL 3.2 accepts it with the same unfinished warning as NATIONAL

OBJECT REFERENCE

reference to an object of a class

4 or 8 depending on LP

object-oriented COBOL; not accepted by GnuCOBOL 3.2

The storage sizes are easy to confirm with FUNCTION LENGTH. This program declares one item of each common usage and prints its length; it was run twice, with the defaults and with -std=ibm.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. SIZES.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  ITEM-01 PIC 9(2) COMP.
       01  ITEM-02 PIC 9(4) COMP.
       01  ITEM-03 PIC 9(5) COMP.
       01  ITEM-04 PIC 9(9) COMP.
       01  ITEM-05 PIC 9(10) COMP.
       01  ITEM-06 PIC 9(18) COMP.
       01  ITEM-07 PIC S9(4) BINARY.
       01  ITEM-08 PIC 9(2) COMP-5.
       01  ITEM-09 PIC 9(5) COMP-5.
       01  ITEM-10 PIC S9(7) PACKED-DECIMAL.
       01  ITEM-11 PIC 9(6) COMP-3.
       01  ITEM-12 PIC S9(18) COMP-3.
       01  ITEM-13 COMP-1.
       01  ITEM-14 COMP-2.
       01  ITEM-15 FLOAT-SHORT.
       01  ITEM-16 FLOAT-LONG.
       01  ITEM-17 PIC 9(7).
       01  ITEM-18 PIC S9(7).
       01  ITEM-19 PIC S9(7)
                   SIGN TRAILING SEPARATE.
       01  ITEM-20 INDEX.
       01  ITEM-21 POINTER.
       PROCEDURE DIVISION.
           DISPLAY "9(2) COMP       " FUNCTION LENGTH(ITEM-01)
           DISPLAY "9(4) COMP       " FUNCTION LENGTH(ITEM-02)
           DISPLAY "9(5) COMP       " FUNCTION LENGTH(ITEM-03)
           DISPLAY "9(9) COMP       " FUNCTION LENGTH(ITEM-04)
           DISPLAY "9(10) COMP      " FUNCTION LENGTH(ITEM-05)
           DISPLAY "9(18) COMP      " FUNCTION LENGTH(ITEM-06)
           DISPLAY "S9(4) BINARY    " FUNCTION LENGTH(ITEM-07)
           DISPLAY "9(2) COMP-5     " FUNCTION LENGTH(ITEM-08)
           DISPLAY "9(5) COMP-5     " FUNCTION LENGTH(ITEM-09)
           DISPLAY "S9(7) PACKED    " FUNCTION LENGTH(ITEM-10)
           DISPLAY "9(6) COMP-3     " FUNCTION LENGTH(ITEM-11)
           DISPLAY "S9(18) COMP-3   " FUNCTION LENGTH(ITEM-12)
           DISPLAY "COMP-1          " FUNCTION LENGTH(ITEM-13)
           DISPLAY "COMP-2          " FUNCTION LENGTH(ITEM-14)
           DISPLAY "FLOAT-SHORT     " FUNCTION LENGTH(ITEM-15)
           DISPLAY "FLOAT-LONG      " FUNCTION LENGTH(ITEM-16)
           DISPLAY "9(7) DISPLAY    " FUNCTION LENGTH(ITEM-17)
           DISPLAY "S9(7) DISPLAY   " FUNCTION LENGTH(ITEM-18)
           DISPLAY "S9(7) SEP       " FUNCTION LENGTH(ITEM-19)
           DISPLAY "INDEX           " FUNCTION LENGTH(ITEM-20)
           DISPLAY "POINTER         " FUNCTION LENGTH(ITEM-21)
           STOP RUN.
9(2) COMP       1
9(4) COMP       2
9(5) COMP       4
9(9) COMP       4
9(10) COMP      8
9(18) COMP      8
S9(4) BINARY    2
9(2) COMP-5     1
9(5) COMP-5     4
S9(7) PACKED    4
9(6) COMP-3     4
S9(18) COMP-3   10
COMP-1          4
COMP-2          8
FLOAT-SHORT     4
FLOAT-LONG      8
9(7) DISPLAY    7
S9(7) DISPLAY   7
S9(7) SEP       8
INDEX           4
POINTER         8

The only line that differs under -std=ibm is 9(2) COMP, which is then 2 bytes instead of 1: the ibm dialect sets binary-size to 2-4-8, matching Enterprise COBOL, while the default dialect uses 1-2-4-8. A COMP-5 item of two digits stays one byte under both settings in GnuCOBOL 3.2, whereas Enterprise COBOL allocates a halfword for S9(1) through S9(4). Code that depends on the size of a one- or two-digit binary field, for example a record layout shared with a mainframe, should be compiled with -std=ibm or -fbinary-size=2-4-8, and should still be checked with FUNCTION LENGTH.

Zoned decimal: USAGE DISPLAY

A numeric item with USAGE DISPLAY is zoned decimal: one digit per byte, in printable form. Each byte holds the character for the digit, so on z/OS (EBCDIC) the digits are F0 to F9 and under GnuCOBOL on ASCII systems they are 30 to 39. The numeric class test for a DISPLAY item checks that the digit nibble of each byte is 0 to 9 and that the zone nibbles and the sign are valid. Zoned decimal is the format of reports, terminals and most text files, and it is the right format for data that people or other systems read; it is the slowest for arithmetic, because the compiler converts it before and after every operation. IBM recommends the computational types for intensive arithmetic.

The sign. A signed DISPLAY item (S in the picture) carries its sign in the zone of one digit byte, the overpunch. Without a SIGN clause that byte is the last one. The valid signs on z/OS are C, A, E and F for positive and D and B for negative; the compiler itself generates C for positive or zero, D for negative and F for an unsigned item. An unsigned item, PIC 9(4), has no sign at all and ignores the sign of what is moved into it: the absolute value is stored. So +1234 is F1 F2 F3 C4 and -1234 is F1 F2 F3 D4 on z/OS, while an unsigned 1234 is F1 F2 F3 F4.

The SIGN clause moves or separates the sign. SIGN IS LEADING puts the overpunch on the first digit; SIGN IS TRAILING is the default and is the same as no clause. SEPARATE CHARACTER puts the sign in a byte of its own, + or -, before or after the digits; the S of the picture is then counted in the size of the item, so PIC S9(4) SIGN LEADING SEPARATE is five bytes. The SIGN clause applies only to DISPLAY and NATIONAL items with an S; it cannot be written on a binary or packed item (the compiler says so, shown below). With SIGN IS SEPARATE on a NATIONAL item the sign is a UTF-16 + or -, and a file described with CODE-SET must use separate signs for every signed numeric item.

This program shows the bytes of every sign form. In GnuCOBOL on ASCII a positive value is not overpunched at all and a negative value sets the high nibble of the last digit to 7; -fsign=EBCDIC selects the mainframe zones instead, on top of ASCII digits.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. SIGNS.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-TRAIL-POS    PIC S9(4) VALUE +1234.
       01  WS-TRAIL-NEG    PIC S9(4) VALUE -1234.
       01  WS-UNSIGNED     PIC 9(4)  VALUE 1234.
       01  WS-LEAD-POS     PIC S9(4) SIGN LEADING VALUE +1234.
       01  WS-LEAD-NEG     PIC S9(4) SIGN LEADING VALUE -1234.
       01  WS-LSEP-POS     PIC S9(4) SIGN LEADING SEPARATE
                           VALUE +1234.
       01  WS-LSEP-NEG     PIC S9(4) SIGN LEADING SEPARATE
                           VALUE -1234.
       01  WS-TSEP-POS     PIC S9(4) SIGN TRAILING SEPARATE
                           VALUE +1234.
       01  WS-TSEP-NEG     PIC S9(4) SIGN TRAILING SEPARATE
                           VALUE -1234.
       PROCEDURE DIVISION.
       MAIN-PARA.
           DISPLAY "S9(4) +1234                     "
               FUNCTION HEX-OF(WS-TRAIL-POS)
           DISPLAY "S9(4) -1234                     "
               FUNCTION HEX-OF(WS-TRAIL-NEG)
           DISPLAY "9(4) 1234                       "
               FUNCTION HEX-OF(WS-UNSIGNED)
           DISPLAY "S9(4) SIGN LEADING +1234        "
               FUNCTION HEX-OF(WS-LEAD-POS)
           DISPLAY "S9(4) SIGN LEADING -1234        "
               FUNCTION HEX-OF(WS-LEAD-NEG)
           DISPLAY "S9(4) SIGN LEADING SEPARATE +   "
               FUNCTION HEX-OF(WS-LSEP-POS)
           DISPLAY "S9(4) SIGN LEADING SEPARATE -   "
               FUNCTION HEX-OF(WS-LSEP-NEG)
           DISPLAY "S9(4) SIGN TRAILING SEPARATE +  "
               FUNCTION HEX-OF(WS-TSEP-POS)
           DISPLAY "S9(4) SIGN TRAILING SEPARATE -  "
               FUNCTION HEX-OF(WS-TSEP-NEG)
           STOP RUN.

Default sign handling (ASCII):

S9(4) +1234                     31323334
S9(4) -1234                     31323374
9(4) 1234                       31323334
S9(4) SIGN LEADING +1234        31323334
S9(4) SIGN LEADING -1234        71323334
S9(4) SIGN LEADING SEPARATE +   2B31323334
S9(4) SIGN LEADING SEPARATE -   2D31323334
S9(4) SIGN TRAILING SEPARATE +  313233342B
S9(4) SIGN TRAILING SEPARATE -  313233342D

With -fsign=EBCDIC the overpunch is the printable character that stands for the EBCDIC zone, the classic text form of a mainframe sign: the digits 1 to 9 become A to I when positive and J to R when negative (the digit 0 becomes { or }), so +1234 ends in D (44) and -1234 in M (4D):

S9(4) +1234                     31323344
S9(4) -1234                     3132334D
9(4) 1234                       31323334
S9(4) SIGN LEADING +1234        41323334
S9(4) SIGN LEADING -1234        4A323334
S9(4) SIGN LEADING SEPARATE +   2B31323334
S9(4) SIGN LEADING SEPARATE -   2D31323334
S9(4) SIGN TRAILING SEPARATE +  313233342B
S9(4) SIGN TRAILING SEPARATE -  313233342D

The last six lines are identical in both runs, because the separate sign is a plain + (2B) or - (2D). That is the form to use for text files that other systems read, and for any file that must look the same on ASCII and EBCDIC machines.

Packed decimal: PACKED-DECIMAL and COMP-3

Packed decimal stores two digits in each byte, one per nibble (four bits), and uses the last nibble for the sign. PACKED-DECIMAL and COMP-3 are synonyms. A field of n digits occupies n plus one nibbles, rounded up to whole bytes, so PIC S9(5)V99 (seven digits) takes four bytes and PIC S9(7) takes four. When the digit count is even the first nibble is unused and set to zero, which is why IBM recommends an odd number of digits: no space is wasted and the generated code is simpler. (Enterprise COBOL’s RULES(NOEVENPACK) option reports packed items with an even digit count.)

The sign nibble is C for positive, D for negative and F for unsigned, the same bit pattern as the zone of a signed DISPLAY item. So +1234 in PIC S9(4) COMP-3 is the three bytes 01 23 4C and -1234 is 01 23 4D. The value 12345 in an unsigned PIC 9(5) COMP-3 is 12 34 5F. Packed data is fixed point for arithmetic purposes and is the usual format of money on the mainframe, because decimal fractions are exact and the field is half the size of the zoned form.

A COMP-3 item always carries a sign nibble, whether or not the picture has an S; storing a negative value in an unsigned packed item stores the absolute value. Enterprise COBOL limits a packed item to 18 digits with ARITH(COMPAT) and 31 with ARITH(EXTEND); GnuCOBOL allows up to 38. GnuCOBOL’s Programmer’s Guide adds COMP-6, an unsigned packed item with no sign nibble at all, which Enterprise COBOL does not have.

Invalid sign and digit nibbles

Nothing stops a program from putting bytes that are not valid packed data into a COMP-3 field, for example through a group move or a REDEFINES, and a field that came from a file or another program may hold anything. The compiler assumes that the data conforms to the PICTURE and USAGE, and results with bad data are undefined: typically a data-exception abend on z/OS (S0C7), garbage in GnuCOBOL. The numeric class test is the way to check, IF WS-PACKED IS NUMERIC. This program redefines a packed field as four bytes and tries five patterns, then does the same for a zoned field:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. BADPACK.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-PACKED       PIC S9(5)V99 COMP-3.
       01  WS-RAW REDEFINES WS-PACKED PIC X(4).
       01  WS-ZONED        PIC S9(5)V99.
       01  WS-ZONED-RAW REDEFINES WS-ZONED PIC X(7).
       PROCEDURE DIVISION.
       MAIN-PARA.
           MOVE X"0001250C" TO WS-RAW
           PERFORM CHECK-PACKED
           MOVE X"0001250F" TO WS-RAW
           PERFORM CHECK-PACKED
           MOVE X"0001250D" TO WS-RAW
           PERFORM CHECK-PACKED
           MOVE X"0A01250C" TO WS-RAW
           PERFORM CHECK-PACKED
           MOVE X"0001250B" TO WS-RAW
           PERFORM CHECK-PACKED
           MOVE "00012A0" TO WS-ZONED-RAW
           IF WS-ZONED IS NUMERIC
               DISPLAY "zoned 00012A0 is NUMERIC"
           ELSE
               DISPLAY "zoned 00012A0 is not NUMERIC"
           END-IF
           MOVE "0001250" TO WS-ZONED-RAW
           IF WS-ZONED IS NUMERIC
               DISPLAY "zoned 0001250 is NUMERIC"
           END-IF
           STOP RUN.
       CHECK-PACKED.
           IF WS-PACKED IS NUMERIC
               DISPLAY FUNCTION HEX-OF(WS-RAW) " NUMERIC, value "
                   WS-PACKED
           ELSE
               DISPLAY FUNCTION HEX-OF(WS-RAW) " not NUMERIC"
           END-IF.
0001250C NUMERIC, value +00012.50
0001250F not NUMERIC
0001250D NUMERIC, value -00012.50
0A01250C not NUMERIC
0001250B not NUMERIC
zoned 00012A0 is not NUMERIC
zoned 0001250 is NUMERIC

With -std=ibm the second line changes. A positive F in a signed packed field is accepted, as the -fhostsign option documents ("allow hexadecimal value F for NUMERIC test of signed PACKED DECIMAL field"), and the value shows as 1250:

0001250C NUMERIC, value +0001250
0001250F NUMERIC, value +0001250
0001250D NUMERIC, value -0001250
0A01250C not NUMERIC
0001250B not NUMERIC
zoned 00012A0 is not NUMERIC
zoned 0001250 is NUMERIC

(The value printed differs, +0001250 against +00012.50, because -std=ibm turns off GnuCOBOL’s pretty-display, which inserts the decimal point on DISPLAY of a numeric item.) On z/OS what the class test accepts depends on NUMPROC and on the installation option NUMCLS, described below; invalid digit nibbles such as A in a digit position are rejected everywhere. The NUMCHECK option (Enterprise COBOL 6.3) can generate such tests automatically for every packed and zoned sending item.

Binary: BINARY, COMP, COMP-4, COMP-5

BINARY, COMP, COMP-4 and COMPUTATIONAL are synonyms: a two’s-complement binary integer, scaled by the picture (a V or P is a power of ten, not a binary fraction). The storage is chosen from the number of digits: one to four digits is a halfword (2 bytes), five to nine a fullword (4 bytes), ten to eighteen a doubleword (8 bytes). On z/OS binary data is big-endian, the machine’s own order, so 12345 is 00 00 30 39. GnuCOBOL also stores COMP big-endian by default, whatever the host, for compatibility with the mainframe; the binary-byteorder configuration option may change it to the native order. It is slower on a little-endian machine, which the GnuCOBOL manual notes under "Optimize binary".

COMP-5 is native binary. It uses the byte order of the machine, and, more importantly, it is not limited to the value implied by the picture: a PIC 9(4) COMP-5 holds 0 to 65535, not 0 to 9999, because its capacity is the size of the storage (2, 4 or 8 bytes), and moves into it truncate at the binary field size. IBM’s ranges for COMP-5 are: S9(1) to S9(4) -32768 to +32767; S9(5) to S9(9) -2147483648 to +2147483647; the S9(10) to S9(18) range is the full signed 64 bits; an unsigned item has the corresponding 0 to 65535, 0 to 4294967295 and 0 to 18446744073709551615. A scaling picture works as expected: PIC S99V99 COMP-5 is a halfword and holds -327.68 to +327.67. COMP-5 is the usage for data that comes from or goes to non-COBOL code (C structures, CALL parameters, Db2 host variables, sockets and so on), where the bytes do not respect a decimal picture. One IBM subtlety: with ON SIZE ERROR a COMP-5 receiver is limited to the value of its decimal picture, not to its binary range.

GnuCOBOL adds COMP-X (big-endian unsigned, or signed with an S), and the C-like BINARY-CHAR, BINARY-SHORT, BINARY-LONG and BINARY-DOUBLE, with SIGNED or UNSIGNED: native binary integers of 1, 2, 4 and 8 bytes with no picture. They are the natural types for CALL interfaces to C (see Interoperability with C, Java and Python); the Enterprise COBOL 6.5 manuals list them only as reserved words.

Layout of binary items

The bytes of the same value differ between COMP, COMP-5 and COMP-X on a little-endian host. This program stores 258 (X'0102') and a 1.5 in every floating-point and pointer type; the first table below uses the binary lines, the later sections the others.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. FLOATS.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-SHORT      COMP-1 VALUE 1.5.
       01  WS-LONG       COMP-2 VALUE 1.5.
       01  WS-FSHORT     FLOAT-SHORT VALUE 1.5.
       01  WS-FLONG      FLOAT-LONG VALUE 1.5.
       01  WS-DEC16      FLOAT-DECIMAL-16 VALUE 1.5.
       01  WS-DEC34      FLOAT-DECIMAL-34 VALUE 1.5.
       01  WS-INDEX      INDEX.
       01  WS-PTR        POINTER.
       01  WS-BIN-X      PIC 9(4) COMP-X VALUE 258.
       01  WS-BIN-NUM    PIC 9(4) COMP VALUE 258.
       01  WS-BIN-NAT    PIC 9(4) COMP-5 VALUE 258.
       PROCEDURE DIVISION.
       MAIN-PARA.
           SET WS-INDEX TO 3
           DISPLAY "COMP-1          " FUNCTION LENGTH(WS-SHORT) " "
               FUNCTION HEX-OF(WS-SHORT)
           DISPLAY "COMP-2          " FUNCTION LENGTH(WS-LONG) " "
               FUNCTION HEX-OF(WS-LONG)
           DISPLAY "FLOAT-SHORT     " FUNCTION LENGTH(WS-FSHORT) " "
               FUNCTION HEX-OF(WS-FSHORT)
           DISPLAY "FLOAT-LONG      " FUNCTION LENGTH(WS-FLONG) " "
               FUNCTION HEX-OF(WS-FLONG)
           DISPLAY "FLOAT-DECIMAL-16" FUNCTION LENGTH(WS-DEC16) " "
               FUNCTION HEX-OF(WS-DEC16)
           DISPLAY "FLOAT-DECIMAL-34" FUNCTION LENGTH(WS-DEC34) " "
               FUNCTION HEX-OF(WS-DEC34)
           DISPLAY "INDEX = 3       " FUNCTION LENGTH(WS-INDEX) " "
               FUNCTION HEX-OF(WS-INDEX)
           DISPLAY "POINTER (null)  " FUNCTION LENGTH(WS-PTR) " "
               FUNCTION HEX-OF(WS-PTR)
           DISPLAY "COMP 258        " FUNCTION LENGTH(WS-BIN-NUM) " "
               FUNCTION HEX-OF(WS-BIN-NUM)
           DISPLAY "COMP-5 258      " FUNCTION LENGTH(WS-BIN-NAT) " "
               FUNCTION HEX-OF(WS-BIN-NAT)
           DISPLAY "COMP-X 258      " FUNCTION LENGTH(WS-BIN-X) " "
               FUNCTION HEX-OF(WS-BIN-X)
           STOP RUN.
COMP-1          4 0000C03F
COMP-2          8 000000000000F83F
FLOAT-SHORT     4 0000C03F
FLOAT-LONG      8 000000000000F83F
FLOAT-DECIMAL-168 0F0000000000A031
FLOAT-DECIMAL-3416 0F000000000000000000000000003E30
INDEX = 3       4 03000000
POINTER (null)  8 0000000000000000
COMP 258        2 0102
COMP-5 258      2 0201
COMP-X 258      2 0102

COMP and COMP-X hold 258 as 01 02 (big-endian) and COMP-5 as 02 01 (native, here little-endian). Do not use COMP-5 in a record that must be read on another architecture, and do not use COMP for an interface that expects the machine’s integers without a conversion.

Truncation: TRUNC and binary-truncate

A binary item has two limits: the number the picture allows (PIC 9(4) COMP means four digits, 0 to 9999) and the number the storage allows (two bytes hold up to 65535 unsigned). What happens to a value between the two, or beyond both, is the subject of the TRUNC option on z/OS and of the binary-truncate setting in GnuCOBOL. It affects BINARY, COMP and COMP-4 receiving fields in MOVE statements and arithmetic; it never affects COMP-5, packed or zoned items, or floating point.

Enterprise COBOL Behaviour GnuCOBOL equivalent

TRUNC(STD) (the default)

the value is truncated to the number of digits in the picture, as a decimal operation: MOVE 123451 TO a PIC S99 COMP item stores 51

binary-truncate: yes, the default dialect setting, -fbinary-truncate

TRUNC(OPT)

a performance option: the compiler assumes the data fits the picture and truncates in whichever way is cheapest, so the result for oversize data cannot be predicted

no equivalent

TRUNC(BIN)

every binary item is handled as COMP-5: truncation only at the halfword, fullword or doubleword boundary, and DISPLAY shows the full content

binary-truncate: no, -fno-trunc (or -fnotrunc)

IBM’s own example is MOVE 123451 TO BIN-VAR for PIC S99 USAGE BINARY: the result is 51 with TRUNC(STD), and -7621 with TRUNC(OPT) or TRUNC(BIN), because the low two bytes of 123451 are X'E23B', which overflows into the sign bit. TRUNC(BIN) is the recommended setting for programs whose binary fields are set by other products (IMS, Db2, C, PL/I), which place values that do not respect the picture. COMP-5 on selected items gives the same behaviour for just those items, without the performance cost of TRUNC(BIN) everywhere.

This program moves 12345 into a four-digit item of each usage, adds one to 9999, and then uses ON SIZE ERROR. It reads 12345 from a variable, because a constant that does not fit the picture is diagnosed at compile time (shown after the example).

       IDENTIFICATION DIVISION.
       PROGRAM-ID. TRUNC.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-BIN     PIC 9(4) COMP.
       01  WS-NATIVE  PIC 9(4) COMP-5.
       01  WS-PACKED  PIC 9(4) COMP-3.
       01  WS-ZONED   PIC 9(4).
       01  WS-BIG     PIC 9(5) VALUE 12345.
       PROCEDURE DIVISION.
       MAIN-PARA.
           MOVE WS-BIG TO WS-BIN WS-NATIVE WS-PACKED WS-ZONED
           DISPLAY "MOVE 12345 TO COMP    : " WS-BIN
           DISPLAY "MOVE 12345 TO COMP-5  : " WS-NATIVE
           DISPLAY "MOVE 12345 TO COMP-3  : " WS-PACKED
           DISPLAY "MOVE 12345 TO DISPLAY : " WS-ZONED
           MOVE 9999 TO WS-BIN WS-NATIVE
           ADD 1 TO WS-BIN WS-NATIVE
           DISPLAY "9999 + 1 in COMP      : " WS-BIN
           DISPLAY "9999 + 1 in COMP-5    : " WS-NATIVE
           MOVE 9999 TO WS-BIN
           ADD 1 TO WS-BIN
               ON SIZE ERROR DISPLAY "SIZE ERROR, COMP left as "
                   WS-BIN
           END-ADD
           STOP RUN.

Default dialect (binary-truncate: yes):

MOVE 12345 TO COMP    : 2345
MOVE 12345 TO COMP-5  : 12345
MOVE 12345 TO COMP-3  : 2345
MOVE 12345 TO DISPLAY : 2345
9999 + 1 in COMP      : 0000
9999 + 1 in COMP-5    : 10000
SIZE ERROR, COMP left as 9999

The binary item is truncated to 2345 and wraps to 0000 on 9999 + 1; the COMP-5 item holds the full values, ON SIZE ERROR fires for the COMP item, which did not raise it for the plain ADD above it, and COMP-3 and DISPLAY truncate to the picture. With -std=ibm the program behaves like TRUNC(BIN):

MOVE 12345 TO COMP    : 12345
MOVE 12345 TO COMP-5  : 12345
MOVE 12345 TO COMP-3  : 2345
MOVE 12345 TO DISPLAY : 2345
9999 + 1 in COMP      : 10000
9999 + 1 in COMP-5    : 10000

This is a real difference between GnuCOBOL and the mainframe: the ibm dialect of GnuCOBOL 3.2 sets binary-truncate to no, whereas Enterprise COBOL’s default is TRUNC(STD). A program that depends on truncation and is compiled for GnuCOBOL with -std=ibm keeps the extra digits, and no size error is raised until the value exceeds the storage. To get the TRUNC(STD) behaviour with that dialect, add -fbinary-truncate; the output then is as in the default dialect, except that DISPLAY pads a binary item to the digits of its size (02345) because pretty-display is off:

MOVE 12345 TO COMP    : 02345
MOVE 12345 TO COMP-5  : 12345
MOVE 12345 TO COMP-3  : 2345
MOVE 12345 TO DISPLAY : 2345
9999 + 1 in COMP      : 00000
9999 + 1 in COMP-5    : 10000
SIZE ERROR, COMP left as 09999

The compile-time diagnostic. A literal that does not fit the picture is reported by cobc -Wall as a truncation, and the program then runs with the truncated value (this example is deliberately wrong):

badlit.cob:7: warning: value size exceeds data size [-Wtruncate]
    5 |        01  WS-BIN          PIC 9(4) COMP.
    6 |        PROCEDURE DIVISION.
    7 >            MOVE 12345 TO WS-BIN
    8 |            DISPLAY WS-BIN
    9 |            STOP RUN.
badlit.cob:7: note: value is 12345 [-Wtruncate]
badlit.cob:5: note: 'WS-BIN' defined here as PIC 9(4) [-Wtruncate]
    3 |        DATA DIVISION.
    4 |        WORKING-STORAGE SECTION.
    5 >        01  WS-BIN          PIC 9(4) COMP.
    6 |        PROCEDURE DIVISION.
    7 |            MOVE 12345 TO WS-BIN
2345

The GnuCOBOL binary options

Three configuration options, settable in a dialect file or on the cobc command line, decide the format of binary items:

Option Values Meaning

-fbinary-size=value

1-2-4-8 (default dialect), 2-4-8 (ibm), 1--8

how many bytes a COMP item gets for its digits; 2-4-8 matches Enterprise COBOL

-fbinary-byteorder=value

big-endian (default), native

byte order of COMP; native is faster on little-endian machines

-fbinary-truncate and -fno-trunc

on and off

decimal truncation to the picture (the TRUNC(STD) behaviour), or no truncation

Related options are -fbinary-comp-1, which makes COMP-1 a 16-bit signed integer (off in the ibm and default dialects), and -fnumeric-pointer, which makes POINTER an unsigned 64-bit integer.

Binary integers are exact

The Taulli book says that binary data is "not good for math because there could be rounding differences". That is not right, and the difference matters when choosing a usage. A binary integer, COMP or COMP-5, is an exact integer in a fixed number of bits; addition, subtraction and multiplication give exact results as long as the result fits, and the scale of a picture such as S9(5)V99 COMP is a decimal scale factor applied to an exact integer, so 12.50 is stored exactly as 1250. What can go wrong with binary is not rounding:

  • Truncation. A result that is larger than the picture (or larger than the storage) is truncated, as above. This is controlled by TRUNC or binary-truncate, and ON SIZE ERROR tests for it.

  • Scaling. Division, and any ROUNDED clause, depend on the digits of the decimal scale, not on the binary representation.

  • Mixing with floating point. An item that went through COMP-1 or COMP-2 is an approximation (next section).

The next example sums 0.10 one thousand times in COMP-3, COMP and COMP-2. The two integer-based types give exactly 100.00. The floating-point sum is not 100, and the same program shows the COMP-2 value of 0.1 after one conversion.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. EXACT.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-FINE-COMP3   PIC S9(5)V99 COMP-3 VALUE +12.50.
       01  WS-FINE-BIN     PIC S9(5)V99 COMP   VALUE +12.50.
       01  WS-FLOAT-S      COMP-1.
       01  WS-FLOAT-L      COMP-2.
       01  WS-SUM-COMP3    PIC S9(5)V99 COMP-3 VALUE ZERO.
       01  WS-SUM-BIN      PIC S9(5)V99 COMP   VALUE ZERO.
       01  WS-SUM-FLOAT    COMP-2 VALUE ZERO.
       01  WS-COUNT        PIC 9(4) COMP VALUE ZERO.
       01  WS-EDIT         PIC -(5)9.99.
       PROCEDURE DIVISION.
       MAIN-PARA.
           PERFORM VARYING WS-COUNT FROM 1 BY 1 UNTIL WS-COUNT > 1000
               ADD 0.10 TO WS-SUM-COMP3
               ADD 0.10 TO WS-SUM-BIN
               ADD 0.10 TO WS-SUM-FLOAT
           END-PERFORM
           MOVE WS-SUM-COMP3 TO WS-EDIT
           DISPLAY "COMP-3 sum of 1000 x 0.10 : " WS-EDIT
           MOVE WS-SUM-BIN TO WS-EDIT
           DISPLAY "COMP   sum of 1000 x 0.10 : " WS-EDIT
           DISPLAY "COMP-2 sum of 1000 x 0.10 : " WS-SUM-FLOAT
           MOVE 0.1 TO WS-FLOAT-S
           MOVE 0.1 TO WS-FLOAT-L
           DISPLAY "COMP-1 0.1 : " WS-FLOAT-S
           DISPLAY "COMP-2 0.1 : " WS-FLOAT-L
           COMPUTE WS-FLOAT-L = WS-FLOAT-L * 3
           DISPLAY "COMP-2 0.1 x 3 : " WS-FLOAT-L
           STOP RUN.
COMP-3 sum of 1000 x 0.10 :    100.00
COMP   sum of 1000 x 0.10 :    100.00
COMP-2 sum of 1000 x 0.10 : 99.99999999999574
COMP-1 0.1 : 0.1
COMP-2 0.1 : 0.09999999999999999
COMP-2 0.1 x 3 : 0.2999999999999999

The result with -std=ibm was the same. A caveat on the digits: GnuCOBOL converts floating-point values through decimal in some operations, so the last digits of such a sum depend on that conversion and on the library, and are not the same as a sum in C. The point is not the digits, it is that fixed-point decimal usage (COMP-3, DISPLAY, or COMP with a scale) is the right choice for money, and floating point is not.

Floating point

COMP-1 is a single-precision (4 bytes) and COMP-2 a double-precision (8 bytes) floating-point number. They take no PICTURE, so there is no way to say how many digits are meaningful; VALUE is allowed. In COBOL 2002, FLOAT-SHORT and FLOAT-LONG are the standard names for the same two types; GnuCOBOL accepts both pairs, Enterprise COBOL 6.5 only COMP-1 and COMP-2 in the USAGE clause (FLOAT-SHORT and FLOAT-LONG are listed as potential reserved words).

The format differs. On z/OS the items are in System z hexadecimal floating point (HFP): sign bit, a 7-bit exponent that counts powers of 16, and a hexadecimal fraction, so 1.5 is 41 18 00 00 (COMP-1) and 41 18 00 00 00 00 00 00 (COMP-2) . GnuCOBOL stores IEEE 754 binary32 and binary64, native byte order: here 1.5 is 00 00 C0 3F and 00 00 00 00 00 00 F8 3F, as the program above printed. Binary data moved between the two systems must be converted; this is a classic migration issue, discussed in Legacy Features and Migration.

COBOL 2014 adds FLOAT-DECIMAL-16 and FLOAT-DECIMAL-34, IEEE 754 decimal floating point (Decimal64 and Decimal128), which keep decimal fractions exact for up to 16 or 34 digits. GnuCOBOL 3.2 accepts them (8 and 16 bytes, as printed above); Enterprise COBOL 6.5 does not list them in its USAGE clause, so they are not portable to the mainframe. GnuCOBOL also accepts FLOAT-EXTENDED, with a warning that its handling is unfinished.

The practical rules are short. Do not use floating point for money or for quantities compared for equality; do not use it as a loop counter. Use it for scientific or statistical values for which a relative error of a part in 10 to the power of 7 (single) or 15 (double) is acceptable. Convert to a decimal item before comparing or printing with a picture. Enterprise COBOL has the further rule that a floating-point item converted to a fixed-point one passes through intermediate precision whose rules fill Appendix A of the Programming Guide.

INDEX, POINTER and procedure pointers

USAGE INDEX defines an index data item: an item that can hold the value of a table index, so the position found by a SEARCH can be saved in a field of the record or a parameter, and restored with SET. It has no picture, may not be used in arithmetic except through SET, and has the size the compiler chooses (4 bytes in GnuCOBOL; the table pages explain how an index relates to subscripts and displacements: Tables and OCCURS). Do not move a subscript into an index data item with MOVE; use SET index-item TO index-name.

USAGE POINTER defines a data pointer, an address that SET ADDRESS OF and ALLOCATE produce and that a CALL can pass. Its size is that of an address: 4 bytes with LP(32), 8 with LP(64); POINTER-32 is always 4 (Enterprise COBOL 6.3 and later, for 31-bit addressable data in a 64-bit program). PROCEDURE-POINTER and FUNCTION-POINTER hold the address of an entry point. In GnuCOBOL 3.2 a pointer is 8 bytes on a 64-bit host and a null POINTER is all zero bytes, as printed above; POINTER-32 is rejected with unknown USAGE: POINTER-32. Pointers are covered in Pointers and Dynamic Storage.

NATIONAL, UTF-8 and NO SIGN

USAGE NATIONAL (with PIC N) holds characters as UTF-16, two bytes each; numeric items can be national too (a national decimal item uses two bytes per digit, and a signed one must have SIGN IS SEPARATE). USAGE UTF-8 (with PIC U, Enterprise COBOL 6.3 and later) holds UTF-8. DISPLAY-1 is the older double-byte (DBCS) type. The character sets, the conversion functions NATIONAL-OF and DISPLAY-OF, and the CODEPAGE option are on National UTF-8 and Code Pages; this page lists only their storage, in the table above. GnuCOBOL 3.2 recognises NATIONAL and UTF-8 and warns that they are unfinished, and its Programmer’s Guide states that NATIONAL is not supported. This program is deliberately a poor choice for portable code:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. NATUTF.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-WIDE         PIC N(5) USAGE NATIONAL.
       01  WS-UTF          PIC X(5) USAGE UTF-8.
       PROCEDURE DIVISION.
           STOP RUN.
natutf.cob:5: warning: handling of USAGE NATIONAL is unfinished; implementation is likely to be changed [-Wunfinished]
    3 |        DATA DIVISION.
    4 |        WORKING-STORAGE SECTION.
    5 >        01  WS-WIDE         PIC N(5) USAGE NATIONAL.
    6 |        01  WS-UTF          PIC X(5) USAGE UTF-8.
    7 |        PROCEDURE DIVISION.
natutf.cob:5: warning: handling of USAGE NATIONAL is unfinished; implementation is likely to be changed [-Wunfinished]
natutf.cob:6: warning: handling of USAGE UTF-8 is unfinished; implementation is likely to be changed [-Wunfinished]
    4 |        WORKING-STORAGE SECTION.
    5 |        01  WS-WIDE         PIC N(5) USAGE NATIONAL.
    6 >        01  WS-UTF          PIC X(5) USAGE UTF-8.
    7 |        PROCEDURE DIVISION.
    8 |            STOP RUN.

COBOL 2023 adds a NO SIGN phrase to USAGE (INCITS summary of the 2023 changes). It is not in the USAGE clause of Enterprise COBOL 6.5, and GnuCOBOL 3.2 rejects it with a syntax error. As the standard is paywalled and not read here, what the phrase means is not described on this page; an unsigned numeric item is written with a picture without an S, as always.

       IDENTIFICATION DIVISION.
       PROGRAM-ID. NOSIGN.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-COUNT        PIC 9(4) USAGE BINARY NO SIGN.
       PROCEDURE DIVISION.
           STOP RUN.
nosign.cob:5: error: syntax error, unexpected NO
    3 |        DATA DIVISION.
    4 |        WORKING-STORAGE SECTION.
    5 >        01  WS-COUNT        PIC 9(4) USAGE BINARY NO SIGN.
    6 |        PROCEDURE DIVISION.
    7 |            STOP RUN.

NUMPROC and the preferred signs

The NUMPROC option decides whether Enterprise COBOL trusts the sign nibbles of zoned and packed data. It has no effect on binary, national decimal or floating-point data.

  • NUMPROC(NOPFD), the default and recommended in most cases: the compiler accepts any valid sign (A to F) and always generates the preferred sign in a receiver (C, D or F).

  • NUMPROC(PFD): the compiler assumes the data already has preferred signs, positive or zero C, negative D, unsigned F (or + and - for a separate sign), and uses whatever it is given. It is faster, but a REDEFINES or a group move can leave a non-preferred sign and give wrong results. IBM’s advice is to use INITIALIZE rather than group moves with NUMPROC(PFD), to use NOPFD if the program calls PL/I or FORTRAN, and that NUMPROC(PFD) and NUMCHECK(ZON(LAX)) cannot be combined.

The result of a numeric class test depends on NUMPROC and on NUMCLS, an installation option: with NUMCLS(PRIM) and NOPFD, signed items accept C, D and F; with NUMCLS(ALT) A to F; with PFD only C, D and positive zero. A system programmer knows the installation setting. GnuCOBOL has no NUMPROC. Its Programmer’s Guide says that a D sign nibble is negative and any other is positive, and the class test is stricter than the computation (above); -fhostsign relaxes it for an F sign.

Looking at the bytes

The quickest way to learn a usage is to look at its bytes. Three tools do that.

  • FUNCTION HEX-OF(item) returns the bytes of any item as a string of hexadecimal digits, twice the length of the item. It is in GnuCOBOL 3.2 (new in that release) and in Enterprise COBOL 6.5, where FUNCTION HEX-OF of a PIC 9(5) COMP-3 holding 12345 is 12345F and of a zoned PIC 9(5) is F1F2F3F4F5. FUNCTION BIT-OF does the same in binary digits, and HEX-TO-CHAR goes the other way.

  • The ISPF editor and browser show hexadecimal with the HEX ON primary command; a data set record appears as three lines, the characters and the high and low nibbles of every byte. z/OS Debugger and a dump show storage the same way.

  • A conventional hex dump on a workstation file (xxd, od -An -tx1).

The program below prints the bytes of +1234.5 and -1234.5 in PIC S9(5)V9 for the formats; the figure after it is drawn from this output (its EBCDIC row has the same digits with the mainframe zones, computed from the rules above and not run here).

       IDENTIFICATION DIVISION.
       PROGRAM-ID. LAYOUT.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-DISPLAY-POS   PIC S9(5)V9 VALUE +1234.5.
       01  WS-DISPLAY-NEG   PIC S9(5)V9 VALUE -1234.5.
       01  WS-PACKED-POS    PIC S9(5)V9 COMP-3 VALUE +1234.5.
       01  WS-PACKED-NEG    PIC S9(5)V9 COMP-3 VALUE -1234.5.
       01  WS-BINARY        PIC S9(5)V9 COMP VALUE +1234.5.
       01  WS-NATIVE        PIC S9(5)V9 COMP-5 VALUE +1234.5.
       01  WS-SEP-LEAD      PIC S9(5)V9 SIGN LEADING SEPARATE
                            VALUE -1234.5.
       01  WS-SEP-TRAIL     PIC S9(5)V9 SIGN TRAILING SEPARATE
                            VALUE +1234.5.
       PROCEDURE DIVISION.
       MAIN-PARA.
           DISPLAY "DISPLAY +   " FUNCTION LENGTH(WS-DISPLAY-POS) " "
               FUNCTION HEX-OF(WS-DISPLAY-POS)
           DISPLAY "DISPLAY -   " FUNCTION LENGTH(WS-DISPLAY-NEG) " "
               FUNCTION HEX-OF(WS-DISPLAY-NEG)
           DISPLAY "COMP-3 +    " FUNCTION LENGTH(WS-PACKED-POS) " "
               FUNCTION HEX-OF(WS-PACKED-POS)
           DISPLAY "COMP-3 -    " FUNCTION LENGTH(WS-PACKED-NEG) " "
               FUNCTION HEX-OF(WS-PACKED-NEG)
           DISPLAY "COMP        " FUNCTION LENGTH(WS-BINARY) " "
               FUNCTION HEX-OF(WS-BINARY)
           DISPLAY "COMP-5      " FUNCTION LENGTH(WS-NATIVE) " "
               FUNCTION HEX-OF(WS-NATIVE)
           DISPLAY "LEAD SEP -  " FUNCTION LENGTH(WS-SEP-LEAD) " "
               FUNCTION HEX-OF(WS-SEP-LEAD)
           DISPLAY "TRAIL SEP + " FUNCTION LENGTH(WS-SEP-TRAIL) " "
               FUNCTION HEX-OF(WS-SEP-TRAIL)
           STOP RUN.
DISPLAY +   6 303132333435
DISPLAY -   6 303132333475
COMP-3 +    4 0012345C
COMP-3 -    4 0012345D
COMP        4 00003039
COMP-5      4 39300000
LEAD SEP -  7 2D303132333435
TRAIL SEP + 7 3031323334352B

The figure puts the bytes side by side. The decimal point is never stored: V is an instruction to the compiler, and the value is the integer 12345 scaled by a tenth.

The bytes of +1234.5 in PIC S9(5)V9 as zoned decimal in EBCDIC and ASCII, packed decimal COMP-3, big-endian binary COMP and little-endian native COMP-5

Figure: the same value, five storage formats; the amber box is the byte that carries the sign.

The output is identical with -std=ibm. The SIGN LEADING SEPARATE and SIGN TRAILING SEPARATE items are seven bytes long (six digits plus the sign byte), the other DISPLAY item is six.

Choosing a usage

The choice follows from where the data lives and what is done with it.

Situation Use Why

Printed or displayed numbers, text files for other systems, reports

DISPLAY (with SIGN IS SEPARATE for files read on other platforms)

readable bytes, portable between ASCII and EBCDIC with a separate sign

Money, quantities with decimals, anything kept in master files

COMP-3 (PACKED-DECIMAL) with an odd digit count

exact decimal fractions, half the size of zoned, the format of the mainframe record layouts

Counters, subscripts, switches, lengths, binary fields of record layouts

COMP with TRUNC(STD), or COMP-5

exact integers, fast; COMP limited to the picture, COMP-5 to the storage

Fields exchanged with C, Java, Db2, IMS, CICS or other products

COMP-5 (or TRUNC(BIN) for the whole program)

the bytes do not respect a decimal picture; native range, no picture truncation

Scientific values with a range

COMP-2 (FLOAT-LONG)

approximate by nature; never for money

Table positions saved in a record

INDEX

the form that SET and SEARCH understand

The Circulation scenario shows the pattern. The member file USERID.CIRC.MEMBMAST (copybook MEMBREC) keeps the outstanding fine in MEMBER-FINES, PIC S9(5)V99, as zoned decimal in a 60-byte record. A program that reads the member master and does arithmetic on the fines can move each value to a packed or binary work item, so the computation does not pay for the zoned conversion. This program reads the sample MEMBMAST (the layout copied from MEMBREC) and shows the fine in each format:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. FINES.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT MEMBER-FILE ASSIGN TO "MEMBMAST"
               ORGANIZATION IS LINE SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD  MEMBER-FILE.
           COPY MEMBREC.
       WORKING-STORAGE SECTION.
       01  WS-EOF                  PIC X VALUE "N".
           88  AT-END              VALUE "Y".
       01  WS-FINE-PACKED          PIC S9(5)V99 COMP-3.
       01  WS-FINE-BINARY          PIC S9(5)V99 COMP.
       01  WS-FINE-NATIVE          PIC S9(5)V99 COMP-5.
       01  WS-FINE-NEG             PIC S9(5)V99.
       PROCEDURE DIVISION.
       MAIN-PARA.
           OPEN INPUT MEMBER-FILE
           PERFORM UNTIL AT-END
               READ MEMBER-FILE
                   AT END SET AT-END TO TRUE
                   NOT AT END PERFORM SHOW-MEMBER
               END-READ
           END-PERFORM
           CLOSE MEMBER-FILE
           STOP RUN.
       SHOW-MEMBER.
           MOVE MEMBER-FINES TO WS-FINE-PACKED WS-FINE-BINARY
               WS-FINE-NATIVE
           COMPUTE WS-FINE-NEG = 0 - MEMBER-FINES
           DISPLAY MEMBER-ID " fine " MEMBER-FINES
           DISPLAY "  DISPLAY " FUNCTION LENGTH(MEMBER-FINES) " "
               FUNCTION HEX-OF(MEMBER-FINES)
           DISPLAY "  COMP-3  " FUNCTION LENGTH(WS-FINE-PACKED) " "
               FUNCTION HEX-OF(WS-FINE-PACKED)
           DISPLAY "  COMP    " FUNCTION LENGTH(WS-FINE-BINARY) " "
               FUNCTION HEX-OF(WS-FINE-BINARY)
           DISPLAY "  COMP-5  " FUNCTION LENGTH(WS-FINE-NATIVE) " "
               FUNCTION HEX-OF(WS-FINE-NATIVE)
           DISPLAY "  negated " FUNCTION HEX-OF(WS-FINE-NEG).
000100 fine +00000.00
  DISPLAY 7 30303030303030
  COMP-3  4 0000000C
  COMP    4 00000000
  COMP-5  4 00000000
  negated 30303030303030
000101 fine +00012.50
  DISPLAY 7 30303031323530
  COMP-3  4 0001250C
  COMP    4 000004E2
  COMP-5  4 E2040000
  negated 30303031323570
000102 fine +00050.00
  DISPLAY 7 30303035303030
  COMP-3  4 0005000C
  COMP    4 00001388
  COMP-5  4 88130000
  negated 30303035303070

Grace Hopper’s fine of 12.50 is 00 01 25 0C packed, 000004E2 big-endian binary (1250 scaled by two decimals), and E2040000 as COMP-5 on this host. Under -std=ibm the DISPLAY of the fine is 0001250+, the digits followed by the sign, because pretty-display is off there. The input is a file of text records, so the positive zoned values are plain digits with no overpunch, and the negated value of 12.50 dumps as 30303031323570: the digits 0001250 with the last one’s high nibble set to 7, GnuCOBOL’s ASCII sign for a negative item.

Where the book is outdated

The Taulli book introduces COMP-3 and BINARY in its chapter 4 with a short table and a few sentences. Three points need correcting or adding. First, binary integers are exact, and its remark that binary data is "not good for math because there could be rounding differences" confuses binary integers with binary floating point; the real concerns are truncation (TRUNC) and decimal scaling, as shown above. Second, it does not mention COMP-5, which is the usage to use when binary data crosses a language or product boundary, or the TRUNC and NUMPROC options that decide how COMP and the signs of zoned and packed data are processed. Third, since the book was written the usage set has grown: Enterprise COBOL 6.3 added UTF-8 and POINTER-32, COBOL 2014 added FLOAT-DECIMAL-16 and FLOAT-DECIMAL-34, and COBOL 2023 added NO SIGN; and the open-source compiler most readers use, GnuCOBOL 3.2, has a different default for truncation in its IBM dialect than the mainframe does.