MOVE and Data Movement
|
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 given. The ISO standard is paywalled and was not read: the rules come from
the vendor manuals. The IBM statements, including everything about S0C7, NUMCHECK and INVDATA, come from the Enterprise COBOL
documentation and were not run here (z/OS; not run here).
MOVE is the most used statement in COBOL and the one with the most rules hidden behind its two-word syntax. It copies data from a
sender to one or more receivers, and what "copies" means depends on the categories of both sides: digits are lined up on the decimal
point, text is lined up on the left, numeric-edited senders are turned back into numbers, group items are copied byte for byte, and
nothing at all is reported when the receiver is too small. This page covers the statement itself, the table of which category may be
moved to which, the alignment and truncation rules, sign handling, group moves, MOVE CORRESPONDING, figurative constants and MOVE ALL,
de-editing, reference modification, and the bugs that come from numeric receivers that do not hold valid numbers, including the
S0C7 abend and the compiler options that detect it. The data categories themselves are defined in
PICTURE Clause, the storage formats in
USAGE and Numeric Representation, and the arithmetic that moves
a computed value into a receiver in Arithmetic and COMPUTE.
The MOVE statement
MOVE has two formats. Format 1 moves one sender to one or more receivers; format 2, MOVE CORRESPONDING (abbreviated MOVE CORR),
moves the elementary items that two group items have in common.
| Format | Written as | Effect |
|---|---|---|
1 |
|
The sending area is moved to each receiver in the order the receivers are written. Every receiver is an identifier; a receiver cannot be an intrinsic function. |
2 |
|
Both operands must be group items. The result is the same as one separate |
The rules that apply to every MOVE:
-
After the statement the sender holds the same data as before, unless the same item is also a receiver.
-
With several receivers the sender is evaluated once and the receivers are filled from left to right. A reference-modified or subscripted sender, or a function that returns text, is evaluated once, immediately before the first receiver is filled; the length, subscripts and reference modification of each receiver are evaluated just before that receiver is filled (see Reference modification as sender and receiver).
-
Items of
USAGE INDEX,POINTER,FUNCTION-POINTER,PROCEDURE-POINTERandOBJECT REFERENCEcannot be named in aMOVE. They can sit inside a group that is moved as a group, andSETis the statement that assigns them (see Pointers and Dynamic Storage). -
Data may be moved to or from a file record area only while the file is open: the record area becomes available when
OPENsucceeds and disappears atCLOSE. -
Overlapping operands give unpredictable results (see below for GnuCOBOL’s warning).
-
When the receiver is a strongly typed group (a
TYPEDEF STRONGitem, Enterprise COBOL 6.5), the sender must be a group of the same type; see REDEFINES, RENAMES and TYPEDEF.
A MOVE is either an elementary move (the receiver is an elementary item and the sender is an elementary item, a literal or a figurative
constant) or a group move (an alphanumeric group is involved on either side). The two follow completely different rules, and most of the
surprises in this page come from using one while thinking of the other.
Categories and the legality table
Every operand of an elementary move belongs to one of the data categories that
PICTURE Clause defines: alphabetic, alphanumeric, alphanumeric-edited, numeric,
numeric-edited, external floating point (USAGE DISPLAY or NATIONAL floating point), internal floating point (COMP-1, COMP-2),
DBCS, national, national-edited and UTF-8. Literals and figurative constants count by what they look like: an alphanumeric literal is
alphanumeric, ZERO and an integer literal are numeric integers, a literal with a decimal point is a numeric non-integer, and SPACE
counts as alphabetic.
The table below is a rewritten summary of Enterprise COBOL’s table of valid and invalid elementary moves (rows are the sender, columns the receiver; "Yes" means the move is valid).
| Sender \ receiver | Alpha-betic | Alpha-numeric | Alnum-edited | Numeric | Numeric-edited | Ext. float | Int. float | DBCS | National | Nat.-edited | UTF-8 |
|---|---|---|---|---|---|---|---|---|---|---|---|
Alphabetic, |
Yes |
Yes |
Yes |
No |
No |
No |
No |
No |
Yes |
Yes |
Yes |
Alphanumeric (a) |
Yes |
Yes |
Yes |
Yes (b) |
Yes (b) |
Yes (c) |
Yes (c) |
No |
Yes |
Yes |
Yes |
Alphanumeric-edited |
Yes |
Yes |
Yes |
No |
No |
No |
No |
No |
Yes |
Yes |
Yes |
Numeric integer, |
No |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
No |
Yes |
Yes |
No |
Numeric non-integer (e) |
No |
No |
No |
Yes |
Yes |
Yes |
Yes |
No |
No |
No |
No |
Numeric-edited |
No |
Yes |
Yes |
Yes |
Yes |
Yes |
Yes |
No |
Yes |
Yes |
Yes |
Floating point (f) |
No |
No |
No |
Yes |
Yes |
Yes |
Yes |
No |
No |
No |
No |
DBCS (g) |
No |
No |
No |
No |
No |
No |
No |
Yes |
Yes |
Yes |
No |
National (h) |
No |
No |
No |
Yes |
Yes |
Yes |
Yes |
No |
Yes |
Yes |
Yes |
National-edited |
No |
No |
No |
No |
No |
No |
No |
No |
Yes |
Yes |
Yes |
UTF-8 (i) |
No |
No |
No |
Yes |
Yes |
Yes |
Yes |
No |
Yes |
Yes |
Yes |
Notes to the table:
-
(a) includes alphanumeric literals and alphanumeric functions.
-
(b) and (c): a figurative constant or alphanumeric literal moved to a numeric, numeric-edited or floating-point receiver must consist only of digits and is treated as a numeric integer; the
ALLliteral cannot be the sender of a move to a floating-point receiver. A data item of category alphanumeric may be the sender, but its content is then taken on trust (see Moving numbers that are not valid). -
(d) includes integer literals; (e) includes non-integer literals; (f) includes floating-point literals and the external and internal floating-point data items; (g) includes DBCS items, DBCS literals and
SPACE; (h) includes national items, national literals, national functions and the figurative constantsZERO,SPACE,QUOTEandALLnational-literal; (i) the same with UTF-8.
Three consequences are worth memorising, because they are the ones that surprise people:
-
A numeric item with decimals cannot be moved to an alphanumeric or alphabetic item. To show or store
18.99as text, move it to a numeric-edited item first (PIC Z(3)9.99) and move that to the text item, or use a numeric-edited receiver andSTRING. -
An integer can be moved to an alphanumeric item (the digits are copied, left-aligned) but not to an alphabetic one.
-
Numeric-edited senders are valid wherever a numeric one is, because the compiler de-edits them (see De-editing), but moving one to an alphanumeric receiver copies the edited characters, comma and sign letters included.
The rules for the code-page conversions of national, UTF-8 and DBCS receivers are in National, UTF-8 and Code Pages.
The program below makes one legal move from each of the interesting sender categories, using the Circulation book price and fine
fields. Note the alphanumeric "001250" moved to PIC 9(4)V99: an alphanumeric sender is treated as an unsigned integer, so the
digits 001250 are the number 1250, not 12.50, and the implied decimal point plays no part in the move.
IDENTIFICATION DIVISION.
PROGRAM-ID. CATS.
* One legal move from each interesting sender category.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-DIGITS PIC X(6) VALUE "001250".
01 W-LETTERS PIC A(6) VALUE "ABCDEF".
01 W-AN PIC X(8).
01 W-AN-EDITED PIC XXBXX/XX.
01 W-COPIES PIC 9(3) VALUE 42.
01 W-NUMERIC PIC 9(4)V99.
01 W-NUM-EDITED PIC Z,ZZ9.99.
01 W-FLOAT COMP-2.
01 W-PRICE PIC 9(4)V99.
01 W-SHOW PIC Z(3)9.99.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE W-DIGITS TO W-NUMERIC
MOVE W-NUMERIC TO W-SHOW
DISPLAY "X(6) digits to 9(4)V99: " W-SHOW
MOVE W-LETTERS TO W-AN
DISPLAY "A(6) to X(8): [" W-AN "]"
MOVE W-LETTERS TO W-AN-EDITED
DISPLAY "A(6) to alphanumeric-edited [" W-AN-EDITED "]"
MOVE W-COPIES TO W-AN
DISPLAY "9(3) integer to X(8): [" W-AN "]"
MOVE W-NUMERIC TO W-NUM-EDITED
DISPLAY "9(4)V99 to numeric-edited: [" W-NUM-EDITED "]"
MOVE W-NUM-EDITED TO W-SHOW
DISPLAY "numeric-edited to numeric: [" W-SHOW "]"
COMPUTE W-FLOAT = 18.99
MOVE W-FLOAT TO W-PRICE
MOVE W-PRICE TO W-SHOW
DISPLAY "COMP-2 18.99 to 9(4)V99: [" W-SHOW "]"
MOVE W-COPIES TO W-FLOAT
MOVE W-FLOAT TO W-PRICE
MOVE W-PRICE TO W-SHOW
DISPLAY "integer to COMP-2 and back: [" W-SHOW "]"
STOP RUN.
Compiled with cobc -x -Wall cats.cob and run it prints (the same lines with -std=ibm):
X(6) digits to 9(4)V99: 1250.00
A(6) to X(8): [ABCDEF ]
A(6) to alphanumeric-edited [AB CD/EF]
9(3) integer to X(8): [042 ]
9(4)V99 to numeric-edited: [1,250.00]
numeric-edited to numeric: [1250.00]
COMP-2 18.99 to 9(4)V99: [ 18.98]
integer to COMP-2 and back: [ 42.00]
The last two lines show another category rule. A COMP-2 item moved to a fixed-point receiver is converted to a decimal value, and
18.99 is not exactly representable in binary floating point: GnuCOBOL 3.2 printed 18.98, so the conversion truncated the binary
value 18.9899999... instead of rounding it. The Enterprise COBOL Programming Guide says that when a COMP-1, COMP-2 or external
floating-point item or a floating-point literal is moved to a fixed-point item, rounding occurs in the low-order position of the receiver,
and that a COMP-2 item moved to a fixed-point item of more than 18 digits keeps only 18 significant digits. The two compilers are not
guaranteed to agree on the last digit, which is one more reason to keep money in fixed-point fields and never in floating point.
Source: Enterprise COBOL 6.5 Language Reference: Elementary moves and Programming Guide: Conversions and precision
Illegal moves and what the compiler says
GnuCOBOL enforces the table at compile time for the clear-cut cases, and warns about literals that cannot be numeric. This program is
deliberately wrong: a non-integer to an alphanumeric item, a non-integer to an alphabetic item, non-numeric text and literals to a numeric
item, and SPACES to a numeric item.
IDENTIFICATION DIVISION.
PROGRAM-ID. ILLEGAL.
* Deliberately wrong: moves that the category rules forbid.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-PRICE PIC 9(4)V99 VALUE 18.99.
01 W-NAME PIC X(10) VALUE "DUNE".
01 W-LETTERS PIC A(10).
01 W-COPIES PIC 9(3).
01 W-ALPHA PIC X(10).
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE W-PRICE TO W-ALPHA
MOVE W-PRICE TO W-LETTERS
MOVE W-NAME TO W-COPIES
MOVE "ABC" TO W-COPIES
MOVE "12A" TO W-COPIES
MOVE ALL "AB" TO W-COPIES
MOVE SPACES TO W-COPIES
MOVE W-NAME TO W-LETTERS
STOP RUN.
With cobc -x -Wall illegal.cob the compiler reports the following (the lines that echo the source are left out), and produces no
executable. The move on line 16, from the alphanumeric data item W-NAME to the numeric W-COPIES, is not reported: whether it makes sense depends
on the content of a data item, not on a literal, and the compiler cannot see that.
illegal.cob: in paragraph 'MAIN-PARAGRAPH':
illegal.cob:14: error: invalid MOVE statement
illegal.cob:15: error: invalid MOVE statement
illegal.cob:17: warning: numeric value is expected [-Wtyping]
illegal.cob:9: note: 'W-COPIES' defined here as PIC 9(3) [-Wtyping]
illegal.cob:18: warning: numeric value is expected [-Wtyping]
illegal.cob:9: note: 'W-COPIES' defined here as PIC 9(3) [-Wtyping]
illegal.cob:19: warning: numeric value is expected [-Wtyping]
illegal.cob:9: note: 'W-COPIES' defined here as PIC 9(3) [-Wtyping]
illegal.cob:20: error: MOVE of figurative constant SPACE to numeric item used
Under -std=ibm the first message becomes a warning, illegal.cob:14: warning: MOVE of non-integer to alphanumeric [-Wothers], and the
move is accepted; the other lines are unchanged. Enterprise COBOL treats a non-integer to alphanumeric move as invalid, so the IBM dialect
setting of GnuCOBOL is more lenient there than the real compiler.
Alignment and truncation
The receiver, not the sender, decides how the data is lined up.
| Receiver | Rule |
|---|---|
Numeric, numeric-edited |
The data is aligned on the (implied) decimal point and padded with zeros or truncated at either end. A receiver without a decimal point is treated as if the point were just to its right, so decimals of the sender are dropped, not rounded. |
Alphanumeric, alphanumeric-edited, alphabetic |
The data is aligned at the left and padded with spaces on the right, or truncated on the right. |
National, national-edited, UTF-8 |
Aligned at the left, padded with national or UTF-8 spaces on the right, and truncated only at a character boundary; the programmer is responsible for not splitting a character made of several encoding units. |
Internal floating point |
A decimal point is assumed at the left; the data is aligned on the first digit position with the exponent adjusted. |
DBCS |
Padded or truncated on the right with DBCS spaces. |
Truncation is silent. A numeric move that does not fit loses its high-order (left) digits and, if there are more decimals than the receiver
holds, its low-order ones; a text move loses its trailing characters. No SIZE ERROR exists for MOVE (only the arithmetic statements have
it), and no condition is raised. The program below moves a PIC 9(5)V99 value to smaller and larger receivers and a 21-character title to
shorter and longer text receivers.
IDENTIFICATION DIVISION.
PROGRAM-ID. ALIGN.
* MOVE alignment and truncation with Circulation fields.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-AMOUNT PIC 9(5)V99 VALUE 12345.67.
01 W-FINE PIC 9(3)V9.
01 W-FINE-INT PIC 9(3).
01 W-BIG-FINE PIC 9(7)V999.
01 W-TITLE-LONG PIC X(30)
VALUE "THE HITCHHIKERS GUIDE".
01 W-TITLE-SHORT PIC X(12).
01 W-TITLE-WIDE PIC X(20).
01 W-COPIES PIC 9(3) VALUE 7.
01 W-COPIES-X PIC X(5).
01 W-COPIES-SHORT PIC X(2).
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE W-AMOUNT TO W-FINE
DISPLAY "12345.67 to 9(3)V9: " W-FINE
MOVE W-AMOUNT TO W-FINE-INT
DISPLAY "12345.67 to 9(3): " W-FINE-INT
MOVE W-AMOUNT TO W-BIG-FINE
DISPLAY "12345.67 to 9(7)V999: " W-BIG-FINE
MOVE W-TITLE-LONG TO W-TITLE-SHORT
DISPLAY "X(30) to X(12): [" W-TITLE-SHORT "]"
MOVE "DUNE" TO W-TITLE-WIDE
DISPLAY "DUNE to X(20): [" W-TITLE-WIDE "]"
MOVE W-COPIES TO W-COPIES-X
DISPLAY "9(3) to X(5): [" W-COPIES-X "]"
MOVE W-COPIES TO W-COPIES-SHORT
DISPLAY "9(3) to X(2): [" W-COPIES-SHORT "]"
STOP RUN.
Compiled with cobc -x -Wall align.cob, it prints:
12345.67 to 9(3)V9: 345.6
12345.67 to 9(3): 345
12345.67 to 9(7)V999: 0012345.670
X(30) to X(12): [THE HITCHHIK]
DUNE to X(20): [DUNE ]
9(3) to X(5): [007 ]
9(3) to X(2): [00]
12345.67 moved to PIC 9(3)V9 keeps the three lowest integer digits and one decimal: 345.6, not 345.7, because the dropped decimal is
cut off and not rounded. Moved to PIC 9(3) it becomes 345. The 21-character title in X(12) loses its last nine characters; the
three-digit copies count 007 moved to X(2) leaves 00, the first two digits, because a numeric integer sent to an alphanumeric receiver
is treated as text. With -std=ibm the same program prints 3456, 345 and 0012345670: the values are identical, but that dialect’s
DISPLAY writes implied-decimal digits without the decimal point (see the notes on DISPLAY in
PICTURE Clause). To compare dialects, print through a numeric-edited item as most
examples on this page do.
The compiler can warn about a literal that is certain to be truncated. This program is deliberately wrong; the literals do not fit:
IDENTIFICATION DIVISION.
PROGRAM-ID. TRUNCLIT.
* Deliberately wrong: literals that do not fit their receivers.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-FINE PIC 9(3)V9.
01 W-CODE PIC X(3).
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE 1234.5 TO W-FINE
MOVE "ABCDE" TO W-CODE
DISPLAY W-FINE " " W-CODE
STOP RUN.
With cobc -x -Wall trunclit.cob GnuCOBOL warns for both moves and still produces an executable that prints the truncated values
(the source-echo lines are left out):
trunclit.cob: in paragraph 'MAIN-PARAGRAPH':
trunclit.cob:10: warning: value size exceeds data size [-Wtruncate]
trunclit.cob:10: note: value is 12345 [-Wtruncate]
trunclit.cob:6: note: 'W-FINE' defined here as PIC 9(3)V9 [-Wtruncate]
trunclit.cob:11: warning: value size exceeds data size [-Wtruncate]
trunclit.cob:11: note: value size is 5 [-Wtruncate]
trunclit.cob:7: note: 'W-CODE' defined here as PIC X(3) [-Wtruncate]
234.5 ABC
The note value is 12345 shows the digit string of the literal 1234.5 without its decimal point: the receiver PIC 9(3)V9 holds four
digits, the literal has five. Whether Enterprise COBOL reports a literal that does not fit is not something this page verifies; a sender that is a data item and
not a literal is truncated without a diagnostic by GnuCOBOL and without a condition by MOVE on z/OS.
Binary receivers and TRUNC
A DISPLAY or packed-decimal receiver always keeps exactly as many digits as its PICTURE allows. A binary (COMP, BINARY, COMP-4)
receiver is different, because a halfword, fullword or doubleword can hold more than the picture’s digits. Which happens is decided by
the compiler option TRUNC in Enterprise COBOL: with TRUNC(STD) (the default) the value is truncated to the digits of the PICTURE,
with TRUNC(OPT) the compiler assumes the data fits and the result of a value that does not is unpredictable, and with TRUNC(BIN) the
item is handled like COMP-5 and keeps the whole binary content. COMP-5 is not affected by TRUNC. Binary integers are exact; what
goes wrong with them is truncation and decimal scaling, not rounding. GnuCOBOL’s equivalent is the pair of dialect settings
binary-truncate and binary-size.
IDENTIFICATION DIVISION.
PROGRAM-ID. BINTRUNC.
* High-order truncation in DISPLAY, packed and binary receivers.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-BIG PIC 9(5) VALUE 12345.
01 W-DISPLAY PIC 9(4).
01 W-PACKED PIC 9(4) COMP-3.
01 W-BINARY PIC 9(4) COMP.
01 W-BINARY5 PIC 9(4) COMP-5.
01 W-SHOW PIC Z(4)9.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE W-BIG TO W-DISPLAY
MOVE W-DISPLAY TO W-SHOW
DISPLAY "PIC 9(4) DISPLAY: " W-SHOW
MOVE W-BIG TO W-PACKED
MOVE W-PACKED TO W-SHOW
DISPLAY "PIC 9(4) COMP-3: " W-SHOW
MOVE W-BIG TO W-BINARY
MOVE W-BINARY TO W-SHOW
DISPLAY "PIC 9(4) COMP: " W-SHOW
MOVE W-BIG TO W-BINARY5
MOVE W-BINARY5 TO W-SHOW
DISPLAY "PIC 9(4) COMP-5: " W-SHOW
STOP RUN.
Compiled with cobc -x -Wall binary.cob (default dialect), the program prints:
PIC 9(4) DISPLAY: 2345
PIC 9(4) COMP-3: 2345
PIC 9(4) COMP: 2345
PIC 9(4) COMP-5: 12345
Under -std=ibm the COMP line changes, because that dialect sets binary-truncate to no and so behaves like TRUNC(BIN), not like the
Enterprise COBOL default TRUNC(STD):
PIC 9(4) DISPLAY: 2345
PIC 9(4) COMP-3: 2345
PIC 9(4) COMP: 12345
PIC 9(4) COMP-5: 12345
Adding -fbinary-truncate to -std=ibm brings it back to 2345 (the output equals the default-dialect run). The full effect of each
option, with the bytes, is in USAGE and Numeric Representation.
Source: Programming Guide: TRUNC
Sign handling
A sign is part of the data only when the receiver has one. The rules for a numeric or numeric-edited receiver are:
-
If the receiver is signed, it receives the sender’s sign, converted to the receiver’s sign representation. If the sender is unsigned, a positive sign is generated.
-
If the receiver is unsigned, no sign is stored and the absolute value of the sender is moved. A negative amount moved to an unsigned field silently becomes positive.
-
A numeric-edited receiver without a sign symbol behaves like an unsigned one:
-12.50is shown as12.50. -
A numeric sender moved to an alphanumeric (or national or UTF-8) receiver contributes its unsigned value; a separate sign character is not moved and the size of the sender counts as one less.
-
An alphanumeric, alphanumeric-edited, national or UTF-8 sender moved to a numeric receiver is treated as an unsigned integer.
The representation of signs in zoned and packed-decimal data, the preferred signs C, D and F, and the effect of NUMPROC are in
USAGE and Numeric Representation. On z/OS an unsigned zoned sender
moved to an alphanumeric receiver keeps its bytes unchanged, even with NUMPROC(NOPFD).
IDENTIFICATION DIVISION.
PROGRAM-ID. SIGNS.
* Sign handling in numeric and numeric-to-text moves.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-CREDIT PIC S9(5)V99 VALUE -12.50.
01 W-FINE-U PIC 9(5)V99.
01 W-FINE-S PIC S9(5)V99.
01 W-FINE-PACKED PIC S9(5)V99 COMP-3.
01 W-SHOW PIC -(5)9.99.
01 W-ADJUST PIC S9(4) VALUE -125.
01 W-ADJUST-SEP PIC S9(4) SIGN IS LEADING SEPARATE.
01 W-ADJUST-TEXT PIC X(8).
01 W-INT-TEXT PIC X(3) VALUE "042".
01 W-INT-NUM PIC S9(3).
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE W-CREDIT TO W-FINE-U
MOVE W-FINE-U TO W-SHOW
DISPLAY "signed -12.50 to unsigned: " W-SHOW
MOVE W-FINE-U TO W-FINE-S
MOVE W-FINE-S TO W-SHOW
DISPLAY "unsigned to signed: " W-SHOW
MOVE W-CREDIT TO W-FINE-PACKED
MOVE W-FINE-PACKED TO W-SHOW
DISPLAY "signed to COMP-3 and back: " W-SHOW
MOVE W-ADJUST TO W-ADJUST-TEXT
DISPLAY "S9(4) -125 to X(8): [" W-ADJUST-TEXT "]"
MOVE W-ADJUST TO W-ADJUST-SEP
MOVE W-ADJUST-SEP TO W-ADJUST-TEXT
DISPLAY "SIGN SEPARATE to X(8): [" W-ADJUST-TEXT "]"
MOVE W-INT-TEXT TO W-INT-NUM
MOVE W-INT-NUM TO W-SHOW
DISPLAY "X(3) '042' to S9(3): " W-SHOW
STOP RUN.
Compiled with cobc -x -Wall signs.cob (the same output with -std=ibm), it prints:
signed -12.50 to unsigned: 12.50
unsigned to signed: 12.50
signed to COMP-3 and back: -12.50
S9(4) -125 to X(8): [0125 ]
SIGN SEPARATE to X(8): [0125 ]
X(3) '042' to S9(3): 42.00
The first two lines are the unsigned rule: -12.50 becomes 12.50 in PIC 9(5)V99 and stays positive when moved on to a signed item. The
third line passes the signed value through a packed-decimal item and keeps the sign. The fourth and fifth lines show -125 moved to a text
item: the digits 0125 appear, left-aligned and space-filled, and the minus sign is gone in both the overpunched and the SIGN IS LEADING
SEPARATE forms.
Group moves
A move is a group move when the sender or the receiver is an alphanumeric group item (a group item that is not a national or UTF-8 group). The compiler then treats that group as plain alphanumeric text. The group move rules are short and unforgiving:
-
The move is treated as an alphanumeric-to-alphanumeric elementary move, except that nothing is converted. The receiving area is filled without regard to the elementary items inside either side.
-
The sender is aligned at the left. If it is shorter than the receiver, the rest of the receiver is filled with spaces, whatever the items there are; if it is longer, the excess on the right is cut off.
-
A group may be moved to an elementary item and an elementary item or literal to a group, in both cases as alphanumeric text. A national group and a UTF-8 group may be moved to each other, and either may be moved to an alphanumeric group.
-
A dynamic-length group cannot be moved to or from another group.
-
The
OCCURSclause is the one exception to "regardless of the elementary items": the length of a table governed byOCCURS DEPENDING ONis evaluated before the move.
The classic accident is a numeric item that is part of the receiving group but lies beyond the end of the sending group. The program
below moves a 30-byte LOAN-RECORD into a 33-byte working group whose last field is a numeric WORK-RETRIES that was just set to zero.
IDENTIFICATION DIVISION.
PROGRAM-ID. GROUPS.
* Group moves are byte copies: no conversion, space padding.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY LOANREC.
01 LOAN-WORK.
05 WORK-TYPE PIC X.
05 WORK-MEMBER-ID PIC 9(6).
05 WORK-BOOK-ID PIC 9(6).
05 WORK-DATE PIC 9(8).
05 WORK-RESERVED PIC X(9).
05 WORK-RETRIES PIC 9(3).
01 LOAN-TEXT PIC X(30).
01 LOAN-SHORT PIC X(12).
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE "O" TO LOAN-TYPE
MOVE 100 TO LOAN-MEMBER-ID
MOVE 1 TO LOAN-BOOK-ID
MOVE 20261020 TO LOAN-DATE
MOVE ZERO TO WORK-RETRIES
MOVE LOAN-RECORD TO LOAN-WORK
DISPLAY "group to longer group: [" LOAN-WORK "]"
IF WORK-RETRIES IS NUMERIC
DISPLAY "WORK-RETRIES is numeric"
ELSE
DISPLAY "WORK-RETRIES is NOT numeric"
END-IF
MOVE LOAN-RECORD TO LOAN-TEXT
DISPLAY "group to X(30): [" LOAN-TEXT "]"
MOVE LOAN-RECORD TO LOAN-SHORT
DISPLAY "group to X(12): [" LOAN-SHORT "]"
MOVE "R000100000001" TO LOAN-RECORD
DISPLAY "X to group: " LOAN-TYPE "/" LOAN-MEMBER-ID "/"
LOAN-BOOK-ID "/[" LOAN-DATE "]"
STOP RUN.
Compiled with cobc -x -Wall -I. groups.cob, the program prints:
group to longer group: [O00010000000120261020 ]
WORK-RETRIES is NOT numeric
group to X(30): [O00010000000120261020 ]
group to X(12): [O00010000000]
X to group: R/000100/000001/[ ]
The record is copied as 30 characters and the three bytes of WORK-RETRIES become spaces: the field that held 000 is not numeric any
more. The short literal moved to LOAN-RECORD last spaces out LOAN-DATE the same way. Moving a group to a shorter text item cuts it
after 12 bytes, in the middle of LOAN-BOOK-ID. Under -std=ibm the output is the same except that the last line shows
[00000000], because that dialect’s DISPLAY of a numeric item holding spaces writes the low digit of the space (the zero); the stored
bytes are still spaces and the class test still fails.
The usual remedies are to build the receiver field by field (the elementary moves convert and edit), to use MOVE CORRESPONDING when the
names match, to move between groups that are exactly the same length, or to set the trailing numeric fields again after the group move
(with MOVE ZERO or INITIALIZE). INITIALIZE is covered with the VALUE clause in
VALUE, INITIALIZE and Condition Names. A group move is the
right tool when two groups really have the same layout, as with MOVE LOAN-RECORD TO LOAN-SAVE, and the copybook is shared so they
cannot drift apart (see Copybooks and REPLACE).
MOVE CORRESPONDING
MOVE CORRESPONDING group-1 TO group-2 moves every pair of subordinate items that correspond, as if each pair had its own MOVE. Two
items correspond when all of these hold:
-
They have the same name and the same qualifiers, up to but not including
group-1andgroup-2. -
At least one of the two is an elementary item, and a
MOVEbetween them is allowed by the table above. InADDandSUBTRACTCORRESPONDINGboth must be elementary numeric items; inMOVEthe numeric-edited, text and numeric categories mix freely under the move rules. -
Neither is
FILLER, a level 66, 77 or 88 item, an index data item or a pointer-like item (POINTER,FUNCTION-POINTER,PROCEDURE-POINTER,OBJECT REFERENCE). -
Neither has a
REDEFINES,RENAMESorOCCURSclause, nor is it aUSAGE INDEXor pointer item. The two group operands themselves may contain or be subordinate to items withREDEFINESorOCCURS. -
Neither group operand is reference-modified, and the name of each matching item is unique after implicit qualification.
Items without a counterpart are left alone, and so are the filler areas of the receiver. The statement is a compile-time shorthand: if no
pair corresponds it does nothing at run time. GnuCOBOL warns about that case (warning: no CORRESPONDING items found [-Wcorresponding]);
this page does not verify whether Enterprise COBOL does.
The first program builds the Circulation member report line from the member master record. The two groups share the names
MEMBER-ID, MEMBER-NAME, MEMBER-FINES and MEMBER-STATUS (names are qualified with OF where the program refers to the master
record); MEMBER-JOINED exists only in the master and LINE-NOTE only in the line.
IDENTIFICATION DIVISION.
PROGRAM-ID. CORR.
* MOVE CORRESPONDING from the member master to a report line.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY MEMBREC.
01 MEMBER-LINE.
05 MEMBER-ID PIC Z(5)9.
05 FILLER PIC X(2) VALUE SPACES.
05 MEMBER-NAME PIC X(30).
05 FILLER PIC X(2) VALUE SPACES.
05 MEMBER-FINES PIC -(5)9.99.
05 FILLER PIC X(2) VALUE SPACES.
05 MEMBER-STATUS PIC X.
05 LINE-NOTE PIC X(10) VALUE "NOT MOVED".
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE 101 TO MEMBER-ID OF MEMBER-RECORD
MOVE "GRACE HOPPER" TO MEMBER-NAME OF MEMBER-RECORD
MOVE 20210301 TO MEMBER-JOINED
MOVE 12.50 TO MEMBER-FINES OF MEMBER-RECORD
SET MEMBER-ACTIVE TO TRUE
MOVE CORRESPONDING MEMBER-RECORD TO MEMBER-LINE
DISPLAY "[" MEMBER-LINE "]"
STOP RUN.
Compiled with cobc -x -Wall -I. corr.cob (and with -std=ibm), it prints one line; the numeric MEMBER-ID and MEMBER-FINES are
edited by their receivers, MEMBER-JOINED has no counterpart so it is not shown, LINE-NOTE keeps its initial value and the FILLER items
stay spaces:
[ 101 GRACE HOPPER 12.50 ANOT MOVED ]
The second program shows what is skipped: a FILLER, an item that has a REDEFINES clause on one side, a table with OCCURS, and an item
that exists in only one group. Everything else, including the nested group LOAN-DATE with its three children, is moved.
IDENTIFICATION DIVISION.
PROGRAM-ID. CORR2.
* What MOVE CORRESPONDING skips: FILLER, OCCURS, REDEFINES,
* and items that exist in only one group.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 LOAN-IN.
05 LOAN-MEMBER-ID PIC 9(6) VALUE 100.
05 LOAN-DATE.
10 LOAN-YEAR PIC 9(4) VALUE 2026.
10 LOAN-MONTH PIC 99 VALUE 10.
10 LOAN-DAY PIC 99 VALUE 20.
05 FILLER PIC X(4) VALUE "FILL".
05 LOAN-FLAG PIC X VALUE "Y".
05 LOAN-FLAG-N REDEFINES LOAN-FLAG PIC 9.
05 LOAN-BOOK PIC 9(6) OCCURS 2 TIMES VALUE 7.
05 LOAN-ONLY-IN PIC X(4) VALUE "ONLY".
01 LOAN-OUT.
05 LOAN-MEMBER-ID PIC 9(6) VALUE ZERO.
05 LOAN-DATE.
10 LOAN-YEAR PIC 9(4) VALUE ZERO.
10 LOAN-MONTH PIC 99 VALUE ZERO.
10 LOAN-DAY PIC 99 VALUE ZERO.
05 FILLER PIC X(4) VALUE "keep".
05 LOAN-FLAG PIC X VALUE "N".
05 LOAN-FLAG-N PIC 9 VALUE 5.
05 LOAN-BOOK PIC 9(6) OCCURS 2 TIMES VALUE ZERO.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE CORRESPONDING LOAN-IN TO LOAN-OUT
DISPLAY "LOAN-OUT: [" LOAN-OUT "]"
STOP RUN.
Compiled with cobc -x -Wall corr2.cob (the same with -std=ibm), it prints:
LOAN-OUT: [00010020261020keepY5000000000000]
Reading the result: LOAN-MEMBER-ID (000100), the date 20261020 and LOAN-FLAG (Y) were moved; the FILLER still holds keep;
LOAN-FLAG-N, which has a REDEFINES, still holds 5; and the OCCURS 2 table LOAN-BOOK still holds zeros, although the sender’s
values were 7. The nested group matched by name, not by position: the children LOAN-YEAR, LOAN-MONTH and LOAN-DAY correspond because they
have the same names and the same qualifier LOAN-DATE.
Why MOVE CORRESPONDING is used with care:
-
A name that is added to either group later starts to correspond with no change to the statement, and a name that is renamed stops corresponding, silently. Review every
CORRESPONDINGwhen a copybook changes. -
Each pair follows the elementary move rules, so a numeric item moved to a smaller one of the same name truncates without a warning.
-
The qualification of the operands has to be spelled out when both groups come from copybooks with identical names, as the example does with
OF. -
GnuCOBOL’s manual describes the effect the same way: as if a series of individual moves were done, one for each match.
Figurative constants and MOVE ALL
A figurative constant is allowed wherever a literal is, except where the syntax forbids it. Its length is that of the associated item, or
one when it stands alone. In a MOVE:
| Sender | Result |
|---|---|
|
Spaces in every position of an alphabetic, alphanumeric, group, national or edited-text receiver. Not allowed with a numeric receiver (the table above). |
|
Numeric zero in a numeric receiver (no sign problem); the character |
|
The highest and lowest character of the collating sequence (hexadecimal |
|
The quotation mark character, repeated. |
|
The string repeated and cut to the receiver’s length, for example |
Singular and plural spellings of SPACE, ZERO, QUOTE, HIGH-VALUE and LOW-VALUE are interchangeable. MOVE ZERO TO group fills a group
with the character 0, including its packed and binary items, which makes them invalid; use INITIALIZE for groups that mix categories. The
words and literals themselves are in
Words, Literals and Figurative Constants.
IDENTIFICATION DIVISION.
PROGRAM-ID. FIGS.
* MOVE with figurative constants and ALL literals.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-RULE PIC X(12).
01 W-CODE PIC X(5).
01 W-COPIES PIC 9(5).
01 W-FINE PIC 9(3)V99.
01 W-SHOW PIC ZZ9.99.
01 W-BUFFER PIC X(4).
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE ALL "-=" TO W-RULE
DISPLAY "ALL ""-="" to X(12): [" W-RULE "]"
MOVE ALL "*" TO W-RULE
DISPLAY "ALL ""*"" to X(12): [" W-RULE "]"
MOVE ZERO TO W-CODE
DISPLAY "ZERO to X(5): [" W-CODE "]"
MOVE SPACES TO W-CODE
DISPLAY "SPACES to X(5): [" W-CODE "]"
MOVE ALL "12" TO W-COPIES
DISPLAY "ALL ""12"" to 9(5): [" W-COPIES "]"
MOVE ZERO TO W-COPIES
DISPLAY "ZERO to 9(5): [" W-COPIES "]"
MOVE ZEROS TO W-FINE
MOVE W-FINE TO W-SHOW
DISPLAY "ZEROS to 9(3)V99: [" W-SHOW "]"
MOVE HIGH-VALUES TO W-BUFFER
DISPLAY "HIGH-VALUES bytes: "
FUNCTION HEX-OF(W-BUFFER)
MOVE LOW-VALUES TO W-BUFFER
DISPLAY "LOW-VALUES bytes: "
FUNCTION HEX-OF(W-BUFFER)
STOP RUN.
Compiled with cobc -x -Wall figs.cob (and with -std=ibm), it prints:
ALL "-=" to X(12): [-=-=-=-=-=-=]
ALL "*" to X(12): [************]
ZERO to X(5): [00000]
SPACES to X(5): [ ]
ALL "12" to 9(5): [21212]
ZERO to 9(5): [00000]
ZEROS to 9(3)V99: [ 0.00]
HIGH-VALUES bytes: FFFFFFFF
LOW-VALUES bytes: 00000000
The line ALL "12" to 9(5) is where the compilers can differ: GnuCOBOL printed 21212, which is the pattern aligned at the right as for any
integer sent to a numeric receiver. The Enterprise COBOL text read for this page says that the literal "must be numeric characters" but
does not state the alignment, so test it on the target compiler before relying on a multi-character pattern in a numeric receiver; a
single digit (MOVE ALL "9") is unambiguous. FUNCTION HEX-OF prints the bytes that HIGH-VALUES and LOW-VALUES put in the item.
De-editing
When the sender of an elementary move is numeric-edited and the receiver is numeric or numeric-edited, the compiler de-edits the sender:
it works out the unedited value (the sign included) that the edited text stands for, and moves that value under the usual numeric rules.
So 1,234.50CR becomes -1234.50 in a PIC S9(5)V99 receiver, 1234.50 in a PIC 9(5)V99 one (the sign is dropped) and $1,234.50 in
another edited item whose picture has no sign position. When the receiver is a text item there is nothing to de-edit: the edited
characters are copied as they are.
IDENTIFICATION DIVISION.
PROGRAM-ID. DEEDIT.
* De-editing: a numeric-edited sender gives back its value,
* but only when the receiver is numeric or numeric-edited.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-FINE-EDITED PIC Z,ZZ9.99CR.
01 W-FINE-NUM PIC S9(5)V99.
01 W-FINE-UNSIGNED PIC 9(5)V99.
01 W-FINE-REPORT PIC $$$,$$9.99.
01 W-FINE-TEXT PIC X(10).
01 W-JOINED PIC 9(8) VALUE 20210301.
01 W-JOINED-EDITED PIC 9999/99/99.
01 W-JOINED-BACK PIC 9(8).
01 W-SHOW PIC -(5)9.99.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE -1234.5 TO W-FINE-EDITED
DISPLAY "edited sender: [" W-FINE-EDITED "]"
MOVE W-FINE-EDITED TO W-FINE-NUM
MOVE W-FINE-NUM TO W-SHOW
DISPLAY "de-edited to S9(5)V99: [" W-SHOW "]"
MOVE W-FINE-EDITED TO W-FINE-UNSIGNED
MOVE W-FINE-UNSIGNED TO W-SHOW
DISPLAY "de-edited to 9(5)V99: [" W-SHOW "]"
MOVE W-FINE-EDITED TO W-FINE-REPORT
DISPLAY "to another edited item: [" W-FINE-REPORT "]"
MOVE W-FINE-EDITED TO W-FINE-TEXT
DISPLAY "to X(10), no de-editing: [" W-FINE-TEXT "]"
MOVE W-JOINED TO W-JOINED-EDITED
DISPLAY "date edited: [" W-JOINED-EDITED "]"
MOVE W-JOINED-EDITED TO W-JOINED-BACK
DISPLAY "date de-edited: [" W-JOINED-BACK "]"
STOP RUN.
Compiled with cobc -x -Wall deedit.cob (the same with -std=ibm), it prints:
edited sender: [1,234.50CR]
de-edited to S9(5)V99: [ -1234.50]
de-edited to 9(5)V99: [ 1234.50]
to another edited item: [ $1,234.50]
to X(10), no de-editing: [1,234.50CR]
date edited: [2021/03/01]
date de-edited: [20210301]
The fifth line is the trap: the edited text, including CR, ends up in the alphanumeric item, and an IF ... IS NUMERIC test or an arithmetic
statement on it would find the comma and the letters. The last two lines show the same de-editing for a yyyymmdd date with / insertion:
the slashes are removed and the number comes back. De-editing is exact only for what the picture preserved, so an edited field that
truncated, rounded or suppressed digits gives back the damaged value. Prefer the original numeric field when it is still available. The
editing symbols and the other uses of the rule are in PICTURE Clause. Intrinsic functions
such as NUMVAL and NUMVAL-C parse numbers from free text and are in
Intrinsic Functions.
Reference modification as sender and receiver
Reference modification name(start:length) selects a substring of an alphanumeric (or national) item and can be used on either side of a
MOVE. The start and length may be arithmetic expressions. A reference-modified operand of a USAGE DISPLAY item is category alphanumeric, whatever the category of the original item
was: moving LOAN-DATE-TEXT(5:2) to a PIC 99 receiver is the alphanumeric-to-numeric move with the digits taken as an unsigned integer.
IDENTIFICATION DIVISION.
PROGRAM-ID. REFMOD.
* Reference modification as source and target of MOVE, and the
* evaluation order of subscripts in a multi-receiver MOVE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 LOAN-DATE-TEXT PIC X(8) VALUE "20261020".
01 W-YEAR PIC X(4).
01 W-MONTH PIC 99.
01 W-ISBN-PLAIN PIC X(13) VALUE "9780140449136".
01 W-ISBN-DASHED PIC X(17) VALUE SPACES.
01 W-LEN PIC 99 VALUE 5.
01 W-TITLE PIC X(20) VALUE "DUNE".
01 W-ITEMS.
05 W-A PIC 9 OCCURS 3 TIMES.
05 W-C PIC 9 OCCURS 3 TIMES VALUE ZERO.
01 W-B PIC 9 VALUE 2.
01 W-SHOW-C PIC X(3).
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
MOVE LOAN-DATE-TEXT(1:4) TO W-YEAR
MOVE LOAN-DATE-TEXT(5:2) TO W-MONTH
DISPLAY "year [" W-YEAR "] month [" W-MONTH "]"
MOVE W-ISBN-PLAIN(1:3) TO W-ISBN-DASHED(1:3)
MOVE W-ISBN-PLAIN(4:1) TO W-ISBN-DASHED(5:1)
MOVE W-ISBN-PLAIN(5:2) TO W-ISBN-DASHED(7:2)
MOVE W-ISBN-PLAIN(7:6) TO W-ISBN-DASHED(10:6)
MOVE W-ISBN-PLAIN(13:1) TO W-ISBN-DASHED(17:1)
MOVE "-" TO W-ISBN-DASHED(4:1) W-ISBN-DASHED(6:1)
W-ISBN-DASHED(9:1) W-ISBN-DASHED(16:1)
DISPLAY "ISBN [" W-ISBN-DASHED "]"
MOVE ALL "*" TO W-TITLE(1:W-LEN)
DISPLAY "title [" W-TITLE "]"
MOVE 3 TO W-A(2)
MOVE W-A(W-B) TO W-B, W-C(W-B)
MOVE W-C(1) TO W-SHOW-C(1:1)
MOVE W-C(2) TO W-SHOW-C(2:1)
MOVE W-C(3) TO W-SHOW-C(3:1)
DISPLAY "W-B now " W-B ", W-C = " W-SHOW-C
STOP RUN.
Compiled with cobc -x -Wall refmod.cob (the same with -std=ibm), it prints:
year [2026] month [10]
ISBN [978-0-14-044913-6]
title [***** ]
W-B now 3, W-C = 003
The first line splits the yyyymmdd text into a year and a month. The second builds a dashed ISBN from the thirteen digits by moving each
group of digits to its position, and the third fills the first five characters of a title with asterisks, the length held in a
data item. The last line is the evaluation-order rule from the beginning of the page: in MOVE W-A(W-B) TO W-B, W-C(W-B) the sender
W-A(2) (value 3) is evaluated once, before the first receiver; the subscript W-B of the second receiver is evaluated only when that
receiver is reached, after W-B has become 3, so the value lands in W-C(3), not in W-C(2). Enterprise COBOL’s manual states the same
rule with the equivalent statements MOVE A(B) TO TEMP, MOVE TEMP TO B, MOVE TEMP TO C(B).
A reference-modified receiver that goes outside the item, or a start or length that is zero or negative, overwrites storage or reads
garbage unless range checking is on: SSRANGE in Enterprise COBOL and -debug (or -fec=bound-ref-mod) in GnuCOBOL; see
Debugging and Troubleshooting.
Overlapping operands
When the sending and receiving areas overlap, the result of a MOVE is unpredictable in Enterprise COBOL; the usual overlap is a
reference-modified item moved onto itself with a shift. GnuCOBOL notices the shift at compile time. This program is deliberately risky:
IDENTIFICATION DIVISION.
PROGRAM-ID. OVERLAP.
* Deliberately risky: sending and receiving areas overlap.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-T PIC X(10) VALUE "ABCDEFGHIJ".
PROCEDURE DIVISION.
MOVE W-T(1:5) TO W-T(2:5)
DISPLAY W-T
MOVE "ABCDEFGHIJ" TO W-T
MOVE W-T(2:5) TO W-T(1:5)
DISPLAY W-T
STOP RUN.
With cobc -x -Wall overlap.cob the compiler warns twice and the program runs:
overlap.cob:8: warning: overlapping MOVE may produce unpredictable results [-Woverlap]
overlap.cob:11: warning: overlapping MOVE may produce unpredictable results [-Woverlap]
AABCDEGHIJ
BCDEFFGHIJ
GnuCOBOL happened to behave like a memory move that is safe for overlaps (AABCDEGHIJ and BCDEFFGHIJ). Do not rely on that, and do not
rely on the opposite on z/OS either: shift text with a separate work item, as in MOVE W-T(1:5) TO W-WORK, MOVE W-WORK TO W-T(2:5).
Moving numbers that are not valid
A numeric receiver does not guarantee that the bytes in it are a number. The compiler trusts the PICTURE and USAGE and generates code
that assumes valid digits and signs; if they are not, the behaviour depends on the compiler and the options, and ranges from wrong
results to an abend.
Invalid numeric data enters a program in a few well-known ways:
-
A record read from a file or message whose numeric column is blank, shorter than the record layout, or contains letters or an edited value.
-
A group move that spaces out a numeric item (the group move example above) or
MOVE SPACESto a group that contains numeric items. -
A
REDEFINESof text over numeric storage, or a numeric field that was never given a value (see VALUE, INITIALIZE and Condition Names). -
An alphanumeric sender moved to a numeric receiver: the compiler cannot check the content.
-
Packed or zoned data produced by another program, or converted wrongly from ASCII to EBCDIC and back.
-
Truncation (for example of a binary field with
TRUNC(OPT)) that leaves a value outside the picture.
What GnuCOBOL does
GnuCOBOL does not abend on invalid data in the default build: spaces count as zero digits, letters contribute their low four bits, and
class tests report the truth. The program below puts a book record with a blank copies column into BOOK-RECORD (the short-record case),
tests it, and uses it.
IDENTIFICATION DIVISION.
PROGRAM-ID. BADNUM.
* How spaces reach a numeric field, and what the class test says.
DATA DIVISION.
WORKING-STORAGE SECTION.
COPY BOOKREC.
01 W-INPUT-LINE PIC X(60).
01 W-TOTAL PIC 9(5) VALUE ZERO.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
* A short input line: the copies column is blank.
MOVE SPACES TO W-INPUT-LINE
MOVE "000001" TO W-INPUT-LINE(1:6)
MOVE W-INPUT-LINE TO BOOK-RECORD
DISPLAY "copies field: [" BOOK-COPIES "]"
IF BOOK-COPIES IS NUMERIC
DISPLAY "BOOK-COPIES is numeric"
ELSE
DISPLAY "BOOK-COPIES is NOT numeric"
END-IF
MOVE BOOK-COPIES TO W-TOTAL
DISPLAY "after MOVE to 9(5): " W-TOTAL
ADD BOOK-COPIES TO W-TOTAL
DISPLAY "after ADD: " W-TOTAL
STOP RUN.
Compiled with cobc -x -Wall -I. invalid.cob, it prints:
copies field: [ ]
BOOK-COPIES is NOT numeric
after MOVE to 9(5): 00000
after ADD: 00000
The class test is the only reliable signal: IS NUMERIC is false for a blank field, and MOVE and ADD carry on with the value zero.
Under -std=ibm the program is silent in the same way, except that the first DISPLAY writes [000] for the blank field.
Compiling with the runtime checks on turns the silent zero into a stop. With -fec=data-incompatible (or -debug, which turns on every
check) the first statement that uses the invalid field as a sender stops the program with exit status 1, here the MOVE on line 21:
copies field: [ ]
BOOK-COPIES is NOT numeric
libcob: invalid.cob:21: error: 'BOOK-COPIES' (Type: NUMERIC DISPLAY) not numeric: ' '
Last statement of "BADNUM" was MOVE
MAIN-PARAGRAPH at invalid.cob:21
Started by ./invalid
EC-DATA-INCOMPATIBLE is one of the exception conditions that the GnuCOBOL manual lists as fatal. It is the closest GnuCOBOL equivalent
of NUMCHECK and, like it, adds run-time cost.
The same program with a packed-decimal and a zoned field made invalid through a REDEFINES over text:
IDENTIFICATION DIVISION.
PROGRAM-ID. BADPACK.
* Invalid packed and zoned data, and what GnuCOBOL does with it.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-RAW PIC X(4) VALUE X"12AC3C4C".
01 W-PACKED REDEFINES W-RAW PIC S9(7) COMP-3.
01 W-ZONED-RAW PIC X(4) VALUE "12A4".
01 W-ZONED REDEFINES W-ZONED-RAW PIC 9(4).
01 W-RESULT PIC S9(7).
01 W-TOTAL PIC 9(5) VALUE ZERO.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
IF W-PACKED IS NUMERIC
DISPLAY "packed field is numeric"
ELSE
DISPLAY "packed field is NOT numeric"
END-IF
IF W-ZONED IS NUMERIC
DISPLAY "zoned field is numeric"
ELSE
DISPLAY "zoned field is NOT numeric"
END-IF
MOVE W-PACKED TO W-RESULT
DISPLAY "packed moved to S9(7): " W-RESULT
ADD W-ZONED TO W-TOTAL
DISPLAY "zoned added to total: " W-TOTAL
STOP RUN.
Compiled with cobc -x -Wall badpack.cob, it prints:
packed field is NOT numeric
zoned field is NOT numeric
packed moved to S9(7): +12:<3<4
zoned added to total: 01214
Both class tests fail, yet the packed field moved to a display field comes out as +12:<3<4, the nibbles A and C in digit positions shown as
the characters : and < (the characters that follow 9 in the character set), and the zoned field with the letter A is added as if it were a digit. With -debug
the program stops at the move (exit status 1):
packed field is NOT numeric
zoned field is NOT numeric
libcob: badpack.cob:24: error: 'W-PACKED' (Type: PACKED-DECIMAL) not numeric: '0x12ac3c4c'
Last statement of "BADPACK" was MOVE
MAIN-PARAGRAPH at badpack.cob:24
ENTRY BADPACK at badpack.cob:13
Started by ./badpack
The numeric class test
The class condition identifier IS NUMERIC checks the content against what is valid for the item’s PICTURE and USAGE: digits 0 to
9 in every digit position, a valid sign in the sign position, and for a zoned item a valid zone code in the upper four bits of every
byte. Leading or trailing spaces make a zoned item non-numeric; a negative sign makes an unsigned item non-numeric. The valid sign values
depend on the NUMPROC option and the installation’s NUMCLS setting. The condition is explained with the other tests in
Conditions, IF and EVALUATE. The guarded move below applies it to five
three-character texts from a transaction file before moving them to PIC 9(3).
IDENTIFICATION DIVISION.
PROGRAM-ID. GUARD.
* Guarding a MOVE to a numeric field with the class test.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 W-TEXTS.
05 FILLER PIC X(3) VALUE "012".
05 FILLER PIC X(3) VALUE " 7".
05 FILLER PIC X(3) VALUE "1A3".
05 FILLER PIC X(3) VALUE SPACES.
05 FILLER PIC X(3) VALUE "-05".
01 W-TABLE REDEFINES W-TEXTS.
05 W-TEXT PIC X(3) OCCURS 5 TIMES.
01 W-IX PIC 9.
01 W-COPIES PIC 9(3).
01 W-ACCEPTED PIC 9 VALUE ZERO.
01 W-REJECTED PIC 9 VALUE ZERO.
PROCEDURE DIVISION.
MAIN-PARAGRAPH.
PERFORM VARYING W-IX FROM 1 BY 1 UNTIL W-IX > 5
IF W-TEXT(W-IX) IS NUMERIC
MOVE W-TEXT(W-IX) TO W-COPIES
ADD 1 TO W-ACCEPTED
DISPLAY "[" W-TEXT(W-IX) "] accepted: " W-COPIES
ELSE
ADD 1 TO W-REJECTED
DISPLAY "[" W-TEXT(W-IX) "] rejected"
END-IF
END-PERFORM
DISPLAY "accepted " W-ACCEPTED ", rejected " W-REJECTED
STOP RUN.
Compiled with cobc -x -Wall guard.cob (the same with -std=ibm), it prints:
[012] accepted: 012
[ 7] rejected
[1A3] rejected
[ ] rejected
[-05] rejected
accepted 1, rejected 4
Only 012 passes. Right-justified ` 7` is not numeric, which surprises people who think of it as the number 7, 1A3 contains a letter,
the blank field is not a number, and -05 fails because the field is an alphanumeric item (PIC X(3)) with a sign character in it, which
is not a digit. The remedy for the first and last is to convert with a function (NUMVAL) or to use UNSTRING/INSPECT (see
STRING, UNSTRING and INSPECT) and then move the result.
What z/OS does: S0C7
On z/OS invalid data in a numeric item typically surfaces as the system completion code S0C7, a data exception program check. The
hardware’s decimal instructions, which Enterprise COBOL uses for zoned and packed-decimal arithmetic and comparisons, raise it when an operand
has an invalid digit or an invalid sign (general z/Architecture behaviour, which the Enterprise COBOL manuals read for this page do not
restate). Which statement fails, and whether any does, depends on the generated code: a MOVE between
two zoned items may be a byte copy that the processor never checks, so the abend often appears later, in the ADD or COMPUTE that finally
uses the field, and the faulty statement is not the one that stored the spaces. The compiler is allowed to assume valid data (that is the
price of NUMPROC(PFD) and NOINVDATA), and the Programming Guide says the results of using an item with invalid data are undefined.
In the Circulation nightly run the typical story is a LOANTRAN record whose date or member id was blank or shifted by a column:
the validation step does an unguarded MOVE to a numeric working item, and the update step abends with an S0C7 at 2 a.m. The usual
steps are to find the failing statement from the dump (offset or statement number in the compile listing, and the contents of the
field in the CEEDUMP), find who put the bad bytes there, and then add the guard shown above. See
Debugging and Troubleshooting for reading the dump and
Error Handling and Declaratives for what the program can do on a
bad record.
NUMCHECK
NUMCHECK makes the Enterprise COBOL compiler generate extra checks so that invalid data is reported where it is used and not only
where an instruction later fails. The default is NONUMCHECK. For each statement it checks the sending operands, not the receivers
(a receiver that is also a sender, as B in ADD A TO B, is checked as a sender). Zoned and packed senders get an implicit numeric
class test, binary senders a size check, and an alphanumeric sender moved to a numeric receiver is treated as a numeric integer and
tested too.
| Suboption | Meaning |
|---|---|
|
An implicit numeric class test for zoned decimal ( |
|
An implicit class test for packed-decimal ( |
|
A check, similar to |
|
|
NUMCHECK alone means NUMCHECK(ZON(ALPHNUM,STRICT),PAC,BIN(TRUNCBIN),MSG). If you name only some data types, the others are off:
NUMCHECK(BIN) is NUMCHECK(NOZON,NOPAC,BIN(TRUNCBIN),MSG), and if every data type is off the listing shows NONUMCHECK. For CALL,
BY CONTENT zoned and packed arguments are checked and BY REFERENCE ones are not. When the compiler can see at compile time that a check
will always find invalid data it issues a compile-time message and drops the run-time check. ZONECHECK is deprecated; it is replaced by
NUMCHECK(ZON(ALPHNUM)). NUMCHECK(ZON(LAX)) cannot be used together with NUMPROC(PFD). The Programming Guide warns that NUMCHECK is
much slower than NONUMCHECK, and that more of the redundant checks are removed at higher OPT levels, so fewer messages appear there.
It is a testing option, not a production setting.
The option is given in the PARM of the compile step or in a CBL (PROCESS) statement at the start of the program. These
two fragments are z/OS; not run here:
//COBOL EXEC IGYWCL,PARM.COBOL='NUMCHECK(ZON,PAC,MSG),TEST'
CBL NUMCHECK(ZON,PAC,ABD)
IDENTIFICATION DIVISION.
PROGRAM-ID. NIGHTLY.
Use MSG while hunting for the records that carry bad data, and ABD in a test where you want the run to stop at the first one. The
procedures and the way compiler options are specified are in
Compiling, Linking and Language Environment and
Performance and Compiler Options.
INVDATA
INVDATA is a compatibility option for programs that were written for COBOL 4 or earlier versions and that depend on what those
compilers did with invalid zoned and packed data. The default is NOINVDATA: the compiler assumes all data in
USAGE DISPLAY and PACKED-DECIMAL items is valid and generates the most efficient code, for example a string comparison where it
can. The Programming Guide recommends NOINVDATA even for programs that process invalid data, and recommends fixing the programs to avoid
the invalid data once NUMCHECK shows where it comes from.
INVDATA without suboptions means INVDATA(NOFORCENUMCMP,CLEANSIGN). The suboptions are:
-
FORCENUMCMPmakes comparisons of zoned decimal items ignore the zone bits of every digit (the value is converted to packed decimal first);NOFORCENUMCMPgenerates comparisons the way the older compilers did, withNUMPROC(NOPFD)orNUMPROC(PFD)as before, so the setting ofNUMPROCin the new program must match the one used with the old compiler. -
CLEANSIGNcleans the sign nibble of zoned and packed items on input to comparisons and toADD,SUBTRACT,MULTIPLYandDIVIDE;NOCLEANSIGNdoes not, which raises the probability of anS0C7when an operand has an invalid sign nibble.CLEANSIGNdoes not apply toSIGN IS SEPARATEitems.
INVDATA affects MOVE statements, comparisons and computations on items that may hold invalid digits, signs or zone bits. The Programming
Guide gives a migration table: from Enterprise COBOL 4.2 or earlier with valid data use NOINVDATA and the old NUMPROC (NUMPROC(MIG)
becomes NOPFD); with invalid data and NUMPROC(MIG) use INVDATA(FORCENUMCMP,NOCLEANSIGN) and NUMPROC(NOPFD); with invalid data and
NUMPROC(NOPFD) or PFD use INVDATA with the same NUMPROC. The table speaks of "COBOL 6.2 or later" while some summaries date the option to 6.3, so check the
What’s New topic of your release before naming one. A zoned item with an invalid zone code is still
non-numeric to the class test even when INVDATA tolerates the zone in comparisons. The wider migration is in
Legacy Features and Migration.
Source: Programming Guide: INVDATA and Programming Guide: NUMPROC
Common MOVE bugs at a glance
| Bug | Symptom | Fix |
|---|---|---|
Receiver too small |
High-order digits or trailing text vanish; no message |
Size the receiver from the copybook, compare lengths in review, use |
Decimals lost |
|
Match the decimals, or round first with |
Sign lost |
A credit becomes a debit in an unsigned receiver |
Declare the receiver |
Alphanumeric to numeric with spaces or letters |
|
Validate with the class test before the move; check |
Short group moved to a longer group |
Trailing numeric items become spaces |
Move field by field, |
Edited field used as a number |
Arithmetic or class test fails on commas and |
Use the unedited source item, or de-edit into a numeric item first |
Non-integer to alphanumeric |
Compile error (invalid |
Move through a numeric-edited item |
Binary field above its |
|
Choose |
|
A new matching name is moved silently |
Re-check every |
Overlapping reference modification |
Unpredictable text |
Use a work item |
Standard, Enterprise COBOL and GnuCOBOL at a glance
| Topic | Enterprise COBOL for z/OS 6.5 | GnuCOBOL 3.2 |
|---|---|---|
Non-integer numeric to alphanumeric |
Invalid |
Error by default; only a warning with |
Integer to alphabetic |
Invalid |
Invalid ( |
|
Invalid |
Error: |
Literal that does not fit the receiver |
Not verified on this page |
|
Binary |
|
Default dialect |
Floating point to fixed point |
Rounding in the low-order position (Programming Guide) |
|
Invalid numeric data at run time |
Typically |
No abend; spaces count as zero; |
Overlapping |
Unpredictable |
|
Notes on the book
Tom Taulli’s Modern Mainframe Development (O’Reilly, 2022) gives MOVE a short section in its COBOL chapter. It presents MOVE as the
COBOL replacement for assignment, shows that a text sender shorter than its receiver is padded with spaces and a longer one is cut off,
that a number is aligned on the decimal point and truncated without rounding, and that several receivers can follow one MOVE. It lists
three category combinations (alphanumeric to numeric, alphanumeric to numeric-edited, numeric to alphanumeric) as the ones allowed when the
sender is an unsigned integer. That is too narrow for the current manuals: the full table above allows many more combinations (signed and
non-integer numeric senders, numeric-edited senders that are de-edited, national and floating-point items), and the "unsigned integer" rule
applies to an alphanumeric sender moved to a number, not to every pair. The book also points to ON SIZE ERROR as protection against
truncation; that phrase belongs to the arithmetic statements, and MOVE has no SIZE ERROR phrase. The book was written against
Enterprise COBOL 6.3 with examples run under GnuCOBOL, and it does not cover MOVE CORRESPONDING, de-editing, the group-move padding rule
or the invalid-data topics at the end of this page: the names NUMCHECK, INVDATA and S0C7 do not appear in it. Its remark that binary
data is risky for arithmetic "because of rounding" is wrong in the sense that matters here: binary integers are exact, and the real issues
are TRUNC truncation and the placement of decimals, as shown above. For the current rules use the Enterprise COBOL 6.5 Language
Reference and Programming Guide and the GnuCOBOL manual listed below.
References
-
Enterprise COBOL for z/OS 6.5 Language Reference: MOVE statement
-
Enterprise COBOL for z/OS 6.5 Language Reference: Elementary moves
-
Enterprise COBOL for z/OS 6.5 Language Reference: Moves involving file record areas
-
Enterprise COBOL for z/OS 6.5 Language Reference: Group moves
-
Enterprise COBOL for z/OS 6.5 Language Reference: Alignment rules
-
Enterprise COBOL for z/OS 6.5 Language Reference: CORRESPONDING phrase
-
Enterprise COBOL for z/OS 6.5 Language Reference: Class condition
-
Enterprise COBOL for z/OS 6.5 Language Reference: Figurative constants
-
Enterprise COBOL for z/OS 6.5 Programming Guide: Conversions and precision
-
Enterprise COBOL for z/OS 6.5 Programming Guide: Checking for incompatible data (numeric class test)
-
Modern Mainframe Development (Tom Taulli, O’Reilly Media, 2022), consulted as a narrative