Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

COBOL  Module 2 – Data definitions: PICTURE, levels, MOVE rules and edited output

Level numbers, group items and MOVE rules

How COBOL level numbers build records from group and elementary items, and what MOVE keeps of digits, signs and text: alignment, truncation and byte copies.

  • Beginner
  • 20 minutes
  • Examples run with GnuCOBOL 3.2 and Python 3.14.8
  • By MySmartCoPilot
Free lesson, Pro answers: 20 minutes
Free: the whole lesson. With a pass: the model answers to its 2 interview questions.

What you will learn

  • Structure records with level numbers 01 to 49, 77 and FILLER
  • Predict truncation, padding and sign loss in numeric and alphanumeric MOVEs
  • Explain why a group MOVE copies bytes without converting the items inside
  • Use MOVE CORRESPONDING, INITIALIZE and OF qualification on records that share names

Before you start

What you’ll learn · Before you start

What you will learn

  • Structure records with level numbers 01 to 49, 77 and FILLER
  • Predict truncation, padding and sign loss in numeric and alphanumeric MOVEs
  • Explain why a group MOVE copies bytes without converting the items inside
  • Use MOVE CORRESPONDING, INITIALIZE and OF qualification on records that share names

Before you start

On this page

Move an interest figure of 1234567.89 from one item into another of PIC 9(3)V99 and the second holds 567.89, and COBOL says nothing: no error at run time and, with -Wall, not even a warning. MOVE is the statement COBOL programs use most, and its rules decide which digits, signs and characters survive the trip.

Below you will build a record from level numbers, groups and FILLER, predict what numeric and alphanumeric MOVEs keep and drop, see how a group MOVE can put spaces into a number, and copy, qualify and clear the items of records that share names.

Level numbers build a record from groups and elementary items

A record is described as a tree. Its first entry has level number: The number that starts a data description entry and places it in a hierarchy: 01 for a record, 02 to 49 for the items inside it, 77 for an item of its own, 66 and 88 for special entries. 01, and every entry under it has a higher number, from 02 to 49. An entry with a PICTURE is an elementary item: A data item with no items under it, described by its own PICTURE clause, such as CUST-ID PIC X(6).. An entry with entries under it is a group item: A data item made of the items under it. It has no PICTURE of its own, its size is the total of theirs, and a MOVE treats it as alphanumeric, byte for byte.: it has no PICTURE of its own, and its size is the total of the items under it. Level 77 describes an elementary item that belongs to no record, such as a counter. Here is a customer record with two groups, five named elementary items and one FILLER: The name of a data item that the program never refers to, such as a gap or a separator inside a record. INITIALIZE leaves FILLER items as they are unless told WITH FILLER., a gap the program never names:

custrec.cob: a customer record and a counter COB · custrec.cob
       IDENTIFICATION DIVISION.
       PROGRAM-ID. CUSTREC.
      * A customer record: two groups, five elementary items, a
      * FILLER, and a 77-level counter outside the record.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       77  REC-COUNT            PIC 9(4)     VALUE ZERO.
       01  CUSTOMER-REC.
           05  CUST-ID          PIC X(6).
           05  CUST-NAME.
               10  FIRST-NAME   PIC X(10).
               10  LAST-NAME    PIC X(12).
           05  BRANCH-CODE      PIC 9(4).
           05  FILLER           PIC X(2)     VALUE SPACES.
           05  BALANCE          PIC S9(7)V99.
       PROCEDURE DIVISION.
           MOVE "AC0417" TO CUST-ID
           MOVE "Asha"   TO FIRST-NAME
           MOVE "Rao"    TO LAST-NAME
           MOVE 42       TO BRANCH-CODE
           MOVE 120.50   TO BALANCE
           ADD 1 TO REC-COUNT
           DISPLAY "CUSTOMER-REC " FUNCTION BYTE-LENGTH(CUSTOMER-REC)
                   " bytes"
           DISPLAY "CUST-NAME    " FUNCTION BYTE-LENGTH(CUST-NAME)
                   " bytes"
           DISPLAY "[" CUSTOMER-REC "]"
           DISPLAY "[" CUST-NAME "]"
           DISPLAY "records so far: " REC-COUNT
           STOP RUN.
CUSTOMER-REC of custrec.cob as nested boxes: two groups, five named items and a FILLER over 43 bytes, and a 77-level counter outside.01 CUSTOMER-REC, bytes 1-4377 REC-COUNT 9(4)outside the record05 CUST-ID X(6)bytes 1-605 CUST-NAME, bytes 7-2805 BRANCH-CODE 9(4)bytes 29-3205 FILLER X(2)bytes 33-3405 BALANCE S9(7)V99bytes 35-4310 FIRST-NAME X(10)bytes 7-1610 LAST-NAME X(12)bytes 17-28counted in

CUSTOMER-REC, item by item, with the bytes each one takes

Text description of the diagram

The record CUSTOMER-REC, level 01, is a group of 43 bytes. Its items, in the order they are stored:

1. CUST-ID, level 05, PIC X(6): bytes 1 to 6. 2. CUST-NAME, level 05, a group of 22 bytes, bytes 7 to 28, which holds FIRST-NAME, level 10, PIC X(10), bytes 7 to 16, and LAST-NAME, level 10, PIC X(12), bytes 17 to 28. 3. BRANCH-CODE, level 05, PIC 9(4): bytes 29 to 32. 4. FILLER, level 05, PIC X(2): bytes 33 and 34, a gap the program never names. 5. BALANCE, level 05, PIC S9(7)V99: bytes 35 to 43.

Below the record, outside it, is REC-COUNT, level 77, PIC 9(4): an elementary item that belongs to no record. A dashed arrow labelled counted in joins the two: the program adds 1 to REC-COUNT for every record it fills.

The run fills the five named items one by one, then displays the sizes and the whole record:

Group sizes, and the record as one string Python · custrec_session.py
from session import files, run

# A record of groups and elementary items: the size of each group and the record as one string of bytes.
files("custrec.cob")
run("""
cobc -x custrec.cob && ./custrec
""")

Output

$ cobc -x custrec.cob && ./custrec
CUSTOMER-REC 43 bytes
CUST-NAME    22 bytes
[AC0417Asha      Rao         0042  000012050]
[Asha      Rao         ]
records so far: 0001

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 custrec_session.py

CUSTOMER-REC is 43 bytes, the 6 + 22 + 4 + 2 + 9 of its items, and CUST-NAME is 22, the 10 + 12 of its two. Displayed as a whole, the record is one string of bytes: the names padded with spaces, BRANCH-CODE as 0042, the FILLER as two spaces, and BALANCE as the nine digits 000012050, with no point in them, because its V takes no byte.

The numbers need not be consecutive. Most programs step by five, 01, 05, 10, which leaves room to slip a level in later, and IBM’s reference allows 1 to 9 in place of 01 to 09. What makes an item part of a group is a higher number than the group’s; IBM notes that “indentation does not affect the magnitude of a level-number”, so the indentation in custrec.cob is only for the reader.

Checkpoint

Check: Level numbers, groups and FILLER

  1. One question If LAST-NAME in CUSTOMER-REC were PIC X(20) instead of PIC X(12), how many bytes would the record take?

    Type a number.

    A hint

    A group is the total of the items under it, and CUSTOMER-REC was 43 bytes.

    Show the answer: One question

    Answer: 51 bytes

    LAST-NAME grows by 8 bytes, so CUST-NAME becomes 30 bytes and CUSTOMER-REC 43 + 8 = 51. Every item after LAST-NAME moves 8 bytes further along the record.

A numeric MOVE lines up the decimal points and cuts at both ends

When both items are numeric, MOVE does arithmetic’s alignment, not a copy. IBM’s rules say it aligns the two items by their decimal points and fills any missing places with zeros. Whatever does not fit is dropped: integer digits on the left, decimal digits on the right, and no rounding is done. If the receiving item has no S, “the absolute value of the sending item is used”. This program tries each case, with two moves from literals and four from variables:

numeric.cob: six numeric MOVEs COB · numeric.cob
       IDENTIFICATION DIVISION.
       PROGRAM-ID. NUMERIC1.
      * Numeric MOVEs line up on the decimal point, then cut what
      * does not fit, at either end, and an unsigned item drops the
      * sign.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  QTY-3        PIC 9(3).
       01  AMT          PIC 9(3)V99.
       01  SAMT         PIC S9(3)V99.
       01  REFUND       PIC S9(5)V9(3) VALUE -42.5.
       01  INTEREST     PIC 9(7)V99    VALUE 1234567.89.
       PROCEDURE DIVISION.
           MOVE 12345 TO QTY-3
           DISPLAY "12345      into 9(3)     " QTY-3
           MOVE 123.456 TO AMT
           DISPLAY "123.456    into 9(3)V99  " AMT
           MOVE 7 TO AMT
           DISPLAY "7          into 9(3)V99  " AMT
           MOVE REFUND TO AMT
           DISPLAY "REFUND     into 9(3)V99  " AMT
           MOVE REFUND TO SAMT
           DISPLAY "REFUND     into S9(3)V99 " SAMT
           MOVE INTEREST TO AMT
           DISPLAY "INTEREST   into 9(3)V99  " AMT
           STOP RUN.

The session builds it twice, keeping only the warning lines: first with -Wall, then with -Wpossible-truncate, which -Wall leaves off. Then it runs the program:

What the two builds warn about, and what the MOVEs keep Python · numeric_session.py
from session import files, run

# Numeric MOVEs: what -Wall reports, what -Wpossible-truncate adds (only the warning lines are kept), and the run.
files("numeric.cob")
run("""
cobc -x -Wall numeric.cob 2>&1 | grep 'warning'
cobc -x -Wpossible-truncate -o numeric-pt numeric.cob 2>&1 | grep 'warning'
./numeric
""")

Output

$ cobc -x -Wall numeric.cob 2>&1 | grep 'warning'
numeric.cob:14: warning: value size exceeds data size [-Wtruncate]
numeric.cob:16: warning: value size exceeds data size [-Wtruncate]
$ cobc -x -Wpossible-truncate -o numeric-pt numeric.cob 2>&1 | grep 'warning'
numeric.cob:20: warning: some digits may be truncated [-Wpossible-truncate]
numeric.cob:22: warning: some digits may be truncated [-Wpossible-truncate]
numeric.cob:24: warning: some digits may be truncated [-Wpossible-truncate]
$ ./numeric
12345      into 9(3)     345
123.456    into 9(3)V99  123.45
7          into 9(3)V99  007.00
REFUND     into 9(3)V99  042.50
REFUND     into S9(3)V99 -042.50
INTEREST   into 9(3)V99  567.89

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 numeric_session.py

-Wall warned about lines 14 and 16, the two literals that do not fit, and about nothing else. -Wpossible-truncate warned about lines 20, 22 and 24, every MOVE from a variable with more digits than its receiver. It reads the PICTUREs, not the values, so it also flagged line 22, where -42.5 fits. The run shows each rule: 12345 kept 345, 123.456 kept 123.45, 7 became 007.00, and REFUND, -42.5, lost its sign in the unsigned AMT. Follow the worst one through the rules:

Interactive · stage by stage MOVE INTEREST TO AMT, digit by digit Step down the column. INTEREST is PIC 9(7)V99 and holds 1234567.89; AMT is PIC 9(3)V99.
MOVE INTEREST TO AMT, digit by digit, step 5 of 5. Showing INTEREST, 9(7)V99, Line up the points, The integer part, The decimal part, AMT holds 567.89. In focus: AMT holds 567.89.INTEREST, 9(7)V99holds 1234567.89Line up the pointsAMT has 3 + 2 placesThe integer part1234 dropped, 567 keptThe decimal part89 kept, nothing droppedAMT holds 567.89no error, no message

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. The value sent. The sending item INTEREST holds 1234567.89, seven digits before its implied decimal point and two after it.
  2. The points line up. A numeric MOVE first lines up the decimal points of the two items. AMT, PIC 9(3)V99, has three places before its point and two after it.
  3. The integer digits. Before the point AMT has room for three digits, so it keeps the three nearest the point, 567. The high-order digits 1234 fall off the left.
  4. The decimal digits. After the point both items have two places, so 89 is copied as it is. A third decimal would have fallen off the right, without rounding, as 123.456 became 123.45.
  5. The result. AMT holds 567.89. The MOVE neither fails nor warns at run time, -Wall says nothing about a MOVE between two variables, and only -Wpossible-truncate flagged this line.

Counting on -Wall to find truncation

Wrong: assuming that a clean -Wall build moves every value intact. Right: build with -Wpossible-truncate as well, and check each warning it gives against the largest value the sending item can hold, here 9,999,999.99 into room for 999.99.

MOVE has no ON SIZE ERROR phrase. The arithmetic statements of a later lesson have one, but MOVE never reports the digits it drops, so the receiver’s PICTURE is the only defence.

Key idea

A numeric MOVE lines up the decimal points, then silently drops whatever falls outside the receiver at either end, and an unsigned receiver keeps no sign.

Checkpoint

Check: What a numeric MOVE keeps

  1. Question 1 of 2 INTEREST, PIC 9(7)V99, holds 1234567.89. What does AMT, PIC 9(3)V99, hold after MOVE INTEREST TO AMT?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: 567.89

    A numeric MOVE aligns the decimal points and drops what does not fit, here the high-order digits 1234, without a message: INTEREST into 9(3)V99 printed 567.89.

  2. Question 2 of 2 REFUND holds -42.5. What does DISPLAY AMT print after MOVE REFUND TO AMT, where AMT is PIC 9(3)V99?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: 042.50

    Without an S in the receiver, a MOVE keeps the size of the number and drops its sign, so a refund of -42.50 would be printed and stored as 42.50.

An alphanumeric MOVE fills from the left, unless JUSTIFIED RIGHT

When the receiver is alphanumeric, MOVE copies characters from the left. A shorter value is padded with spaces on the right, and a longer one loses its characters on the right. The JUSTIFIED RIGHT clause turns both ends round: IBM’s reference says the value is aligned at the right, “the unused character positions at the left are filled with spaces”, and from a longer value “the leftmost character positions are truncated”. A whole number moved into text arrives as its digits:

alnum.cob: text into a plain and a JUSTIFIED RIGHT item COB · alnum.cob
       IDENTIFICATION DIVISION.
       PROGRAM-ID. ALNUM.
      * Alphanumeric MOVEs fill from the left and pad or cut on the
      * right; JUSTIFIED RIGHT does both on the left instead.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  STATION      PIC X(10).
       01  STATION-R    PIC X(10) JUSTIFIED RIGHT.
       01  BRANCH       PIC 9(4)  VALUE 42.
       01  BRANCH-TEXT  PIC X(6).
       PROCEDURE DIVISION.
           MOVE "PUNE" TO STATION
           DISPLAY "PUNE            into X(10)     [" STATION "]"
           MOVE "CHENNAI CENTRAL" TO STATION
           DISPLAY "CHENNAI CENTRAL into X(10)     [" STATION "]"
           MOVE "PUNE" TO STATION-R
           DISPLAY "PUNE            into JUSTIFIED [" STATION-R "]"
           MOVE "CHENNAI CENTRAL" TO STATION-R
           DISPLAY "CHENNAI CENTRAL into JUSTIFIED [" STATION-R "]"
           MOVE BRANCH TO BRANCH-TEXT
           DISPLAY "BRANCH 9(4)     into X(6)      [" BRANCH-TEXT "]"
           STOP RUN.
Padding and cutting at either end Python · alnum_session.py
from session import files, run

# Alphanumeric MOVEs into a plain and a JUSTIFIED RIGHT item, and a number moved into text.
files("alnum.cob")
run("""
cobc -x alnum.cob && ./alnum
""")

Output

$ cobc -x alnum.cob && ./alnum
PUNE            into X(10)     [PUNE      ]
CHENNAI CENTRAL into X(10)     [CHENNAI CE]
PUNE            into JUSTIFIED [      PUNE]
CHENNAI CENTRAL into JUSTIFIED [AI CENTRAL]
BRANCH 9(4)     into X(6)      [0042  ]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 alnum_session.py

MOVE Into a plain PIC X(10) Into PIC X(10) JUSTIFIED RIGHT
PUNE, 4 characters PUNE and 6 spaces 6 spaces and PUNE
CHENNAI CENTRAL, 15 characters CHENNAI CE AI CENTRAL

The last line moved BRANCH, PIC 9(4) holding 42, into a PIC X(6) item and got 0042 and two spaces: the four digit characters, padded on the right like any text. JUSTIFIED is only for receivers of text; IBM does not allow it on numeric or edited items, and it does not change how a VALUE clause sets the item at the start.

A group MOVE copies bytes and converts nothing

A group is alphanumeric, whatever its items are. IBM’s reference treats a MOVE to or from a group “as though it were an alphanumeric-to-alphanumeric elementary move”, so “there is no conversion of data”: the bytes are copied from the left, padded or cut, and each item inside gets whatever bytes land on it. Here an order line is a group of a 3-byte code and a 4-digit quantity:

groupmove.cob: two group MOVEs into an order line COB · groupmove.cob
       IDENTIFICATION DIVISION.
       PROGRAM-ID. GROUPMOVE.
      * A group MOVE copies bytes, left to right, and converts
      * nothing: the items inside get whatever bytes land on them.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  ORDER-LINE.
           05  ITEM-CODE    PIC X(3).
           05  QTY          PIC 9(4).
       PROCEDURE DIVISION.
           MOVE "ABC0012" TO ORDER-LINE
           DISPLAY "ABC0012 -> code [" ITEM-CODE "] qty [" QTY "]"
           MOVE "AB12" TO ORDER-LINE
           DISPLAY "AB12    -> code [" ITEM-CODE "] qty [" QTY "]"
           IF QTY IS NUMERIC
               DISPLAY "QTY is numeric"
           ELSE
               DISPLAY "QTY is not numeric"
           END-IF
           ADD 1 TO QTY
           DISPLAY "after ADD 1 TO QTY: [" QTY "]"
           STOP RUN.
A quantity that is not a number, with and without -debug Python · groupmove_session.py
from session import files, run

# A group MOVE that leaves spaces in a numeric item, what ADD then does, and the same program built with -debug.
files("groupmove.cob")
run("""
cobc -x groupmove.cob && ./groupmove
!cobc -x -debug -o groupmove-debug groupmove.cob && ./groupmove-debug
""")

Output

$ cobc -x groupmove.cob && ./groupmove
ABC0012 -> code [ABC] qty [0012]
AB12    -> code [AB1] qty [2   ]
QTY is not numeric
after ADD 1 TO QTY: [2001]
$ cobc -x -debug -o groupmove-debug groupmove.cob && ./groupmove-debug
ABC0012 -> code [ABC] qty [0012]
AB12    -> code [AB1] qty [2   ]
QTY is not numeric
libcob: groupmove.cob:20: error: 'QTY' (Type: NUMERIC DISPLAY) not numeric: '2   '

 Last statement of "GROUPMOVE" was ADD at line 20 of groupmove.cob
	ENTRY GROUPMOVE at groupmove.cob:11
 Started by ./groupmove-debug
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 groupmove_session.py

ABC0012 split cleanly into ABC and 0012, only because its bytes happened to line up with the items. AB12 did not: the quantity received the digit 2 and three spaces, IS NUMERIC said it was not a number, and ADD 1 TO QTY gave 2001 without a word. Step through where each byte went:

Interactive · stage by stage MOVE "AB12" TO ORDER-LINE, byte by byte Step down the column. ORDER-LINE is a group of 7 bytes, ITEM-CODE, PIC X(3), then QTY, PIC 9(4).
MOVE "AB12" TO ORDER-LINE, byte by byte, step 5 of 5. Showing The literal AB12, Padded with spaces, ITEM-CODE, bytes 1-3, QTY, bytes 4-7, ADD 1 TO QTY. In focus: ADD 1 TO QTY.The literal AB124 bytes for a 7-byte groupPadded with spacesAB12 and three spacesITEM-CODE, bytes 1-3gets AB1QTY, bytes 4-7gets 2 and three spacesADD 1 TO QTY2001, or a stop with -debug

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. An alphanumeric MOVE. The literal has 4 bytes and the group has 7. A group counts as alphanumeric, so this is an alphanumeric MOVE, and nothing looks at the items inside the group.
  2. Padding. Like any alphanumeric MOVE into a longer item, it pads on the right, so the 7 bytes become A, B, 1, 2 and three spaces.
  3. The code. ITEM-CODE is the first 3 bytes of the group, so it now holds AB1. Its last letter is the 1 meant for the quantity.
  4. The quantity. QTY is the last 4 bytes, the digit 2 and three spaces. It is a numeric item that no longer holds a number, so IS NUMERIC is false.
  5. The arithmetic. ADD 1 TO QTY gave 2001 without a word, because the arithmetic read each space as a zero. The program built with -debug stopped instead, with 'QTY' not numeric.

Moving a record into a group of another layout

Wrong: MOVE INPUT-REC TO ORDER-LINE when the two layouts differ, trusting the items to sort themselves out. Right: move item by item, or with MOVE CORRESPONDING, so each item is converted on its way, and test numeric items with IS NUMERIC where the data comes in.

The same program built with -debug stopped at the ADD instead, with ‘QTY’ (Type: NUMERIC DISPLAY) not numeric: ’2 ’ and exit status 1. A silent 2001 in a nightly batch and a stopped job are both caused by the group MOVE; only the second one tells you.

Key idea

A group MOVE is a byte copy: the items inside get the bytes that land on them, digits or not, and nothing is converted or checked.

Checkpoint

Check: Alphanumeric MOVEs and JUSTIFIED · Group MOVEs copy bytes

  1. Question 1 of 2 STATION-R is PIC X(10) JUSTIFIED RIGHT. What does it hold after MOVE "CHENNAI CENTRAL" TO STATION-R?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: AI CENTRAL

    A plain alphanumeric MOVE pads and cuts on the right; JUSTIFIED RIGHT does both on the left, so the session printed [AI CENTRAL].

  2. Question 2 of 2 ORDER-LINE is ITEM-CODE, PIC X(3), then QTY, PIC 9(4). What does QTY hold after MOVE "AB12" TO ORDER-LINE?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: The digit 2 and three spaces

    The receiving item is a group, so the MOVE is alphanumeric: A, B and 1 went to ITEM-CODE, and the 2 and three padding spaces to QTY, which then failed IS NUMERIC.

MOVE CORRESPONDING pairs items by name

Two records often share item names, an input line and an output line with a QTY and an ITEM-CODE each. MOVE CORRESPONDING moves every elementary item of the first group to the item of the same name in the second, and IBM’s reference says the result is “the same as if each pair of CORRESPONDING identifiers were referenced in a separate MOVE statement”, so each pair follows the rules above. Items without a partner are left alone. When a name is used twice, a plain reference to it is ambiguous, so it takes qualification: Making a name that is used twice unique by naming a group that contains it, with OF or IN, as in QTY OF OUT-REC.: QTY OF OUT-REC, or QTY IN OUT-REC, names the group that holds it:

corresponding.cob: two records that share names COB · corresponding.cob
       IDENTIFICATION DIVISION.
       PROGRAM-ID. CORRESP.
      * MOVE CORRESPONDING pairs items by name; a group MOVE between
      * the same two records copies bytes.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  IN-REC.
           05  ITEM-CODE    PIC X(3).
           05  FILLER       PIC X        VALUE "|".
           05  QTY          PIC 9(2).
           05  PRICE        PIC 9(3)V99.
       01  OUT-REC.
           05  QTY          PIC 9(5).
           05  FILLER       PIC X(2)     VALUE "--".
           05  ITEM-CODE    PIC X(5).
           05  NOTE-TEXT    PIC X(4)     VALUE "kept".
       PROCEDURE DIVISION.
           MOVE "XYZ" TO ITEM-CODE OF IN-REC
           MOVE 7     TO QTY OF IN-REC
           MOVE 19.50 TO PRICE
           DISPLAY "IN-REC          [" IN-REC "]"
           MOVE CORRESPONDING IN-REC TO OUT-REC
           DISPLAY "CORRESPONDING   [" OUT-REC "]"
           DISPLAY "QTY OF OUT-REC  " QTY OF OUT-REC
           MOVE IN-REC TO OUT-REC
           DISPLAY "group MOVE      [" OUT-REC "]"
           STOP RUN.

The session runs it, then builds a program that names QTY without saying which one:

CORRESPONDING, a group MOVE and an ambiguous name Python · corresponding_session.py
from session import files, run

# MOVE CORRESPONDING against a group MOVE between the same two records, and a name used without qualification.
files("corresponding.cob", "ambiguous.cob")
run("""
cobc -x corresponding.cob && ./corresponding
!cobc -x ambiguous.cob
""")

Output

$ cobc -x corresponding.cob && ./corresponding
IN-REC          [XYZ|0701950]
CORRESPONDING   [00007--XYZ  kept]
QTY OF OUT-REC  00007
group MOVE      [XYZ|0701950     ]
$ cobc -x ambiguous.cob
ambiguous.cob:11: error: 'QTY' is ambiguous; needs qualification
    9 |            05  QTY          PIC 9(5).
   10 |        PROCEDURE DIVISION.
   11 >            MOVE QTY TO QTY OF OUT-REC
   12 |            DISPLAY QTY IN OUT-REC
   13 |            STOP RUN.
ambiguous.cob:7: note: 'QTY IN IN-REC' defined here
    5 |        WORKING-STORAGE SECTION.
    6 |        01  IN-REC.
    7 >            05  QTY          PIC 9(2)     VALUE 7.
    8 |        01  OUT-REC.
    9 |            05  QTY          PIC 9(5).
ambiguous.cob:9: note: 'QTY IN OUT-REC' defined here
    7 |            05  QTY          PIC 9(2)     VALUE 7.
    8 |        01  OUT-REC.
    9 >            05  QTY          PIC 9(5).
   10 |        PROCEDURE DIVISION.
   11 |            MOVE QTY TO QTY OF OUT-REC
[exit status 1]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 corresponding_session.py

MOVE CORRESPONDING moved QTY and ITEM-CODE: 07 became 00007 in a 5-digit QTY, and XYZ was padded to five characters. PRICE has no partner in OUT-REC, NOTE-TEXT has none in IN-REC, and the FILLERs are never paired, so kept and the two dashes stayed. The plain group MOVE between the same records copied 11 bytes and padded them, so OUT-REC’s QTY now holds XYZ|0, which is not a number. The build of ambiguous.cob failed with ‘QTY’ is ambiguous; needs qualification, and listed both QTY IN IN-REC and QTY IN OUT-REC.

Statement OUT-REC afterwards QTY OF OUT-REC
MOVE CORRESPONDING IN-REC TO OUT-REC 00007–XYZ, 2 spaces, kept 00007, a number
MOVE IN-REC TO OUT-REC XYZ|0701950 and 5 spaces XYZ|0, not a number

INITIALIZE clears a record by category

INITIALIZE sets every elementary item of a group by its category: IBM’s rules make SPACE the value for alphabetic and alphanumeric items and ZERO for numeric ones. FILLER items are left as they are unless the statement says WITH FILLER, and REPLACING names other values for a category. This program clears a customer record three ways:

initialize.cob: a record cleared three ways COB · initialize.cob
       IDENTIFICATION DIVISION.
       PROGRAM-ID. INITREC.
      * INITIALIZE clears a record by category and leaves FILLER
      * alone; REPLACING picks other values.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  CUST-REC.
           05  CUST-ID      PIC X(6)     VALUE "AC0001".
           05  FILLER       PIC X        VALUE "|".
           05  CUST-NAME    PIC X(8)     VALUE "ASHA RAO".
           05  FILLER       PIC X        VALUE "|".
           05  BALANCE      PIC S9(5)V99 VALUE 120.75.
           05  BRANCH       PIC 9(4)     VALUE 42.
       PROCEDURE DIVISION.
           DISPLAY "before         [" CUST-REC "]"
           INITIALIZE CUST-REC
           DISPLAY "INITIALIZE     [" CUST-REC "]"
           INITIALIZE CUST-REC REPLACING ALPHANUMERIC BY "?"
                                         NUMERIC BY 9
           DISPLAY "REPLACING      [" CUST-REC "]"
           INITIALIZE CUST-REC WITH FILLER
           DISPLAY "WITH FILLER    [" CUST-REC "]"
           STOP RUN.
The record before and after each INITIALIZE Python · initialize_session.py
from session import files, run

# A customer record before and after INITIALIZE, INITIALIZE REPLACING and INITIALIZE WITH FILLER.
files("initialize.cob")
run("""
cobc -x initialize.cob && ./initialize
""")

Output

$ cobc -x initialize.cob && ./initialize
before         [AC0001|ASHA RAO|00120750042]
INITIALIZE     [      |        |00000000000]
REPLACING      [?     |?       |00009000009]
WITH FILLER    [                00000000000]

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 initialize_session.py

Plain INITIALIZE turned the names into spaces and BALANCE and BRANCH into zeros, and kept both | separators, because they are FILLER. REPLACING ALPHANUMERIC BY “?” NUMERIC BY 9 put ? and spaces into the text items and 9 into the numbers, 00009.00 and 0009, while the separators stayed again. WITH FILLER cleared them too. A MOVE SPACES TO CUST-REC would have done something else: a group MOVE puts spaces into BALANCE and BRANCH as well, the non-numbers of the previous section.

Clear a record with INITIALIZE, not MOVE SPACES

INITIALIZE gives every numeric item a valid zero; MOVE SPACES to the group leaves spaces in them. Use the first between records, and keep separators and other fixed text as FILLER with a VALUE, which INITIALIZE does not touch.

IBM’s rules also leave out items under a REDEFINES clause, the subject of the next lesson. And an item with no VALUE clause starts, in IBM’s words, with a value that “is unpredictable”, so an INITIALIZE before first use is the portable way to start from a known state.

Checkpoint

Check: MOVE CORRESPONDING and qualification · INITIALIZE by category

  1. Question 1 of 2 Which items of OUT-REC does MOVE CORRESPONDING IN-REC TO OUT-REC change?

    Choose every answer that is right.

    Show the answer: Question 1 of 2

    Answer:

    • ITEM-CODE
    • QTY

    MOVE CORRESPONDING works name by name: QTY and ITEM-CODE are in both records and are moved, each with the full MOVE rules; NOTE-TEXT, PRICE and the FILLERs are left alone.

  2. Question 2 of 2 What did plain INITIALIZE CUST-REC do to the two | separators, which are FILLER items with VALUE "|"?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Nothing; they stayed

    INITIALIZE clears the named items by category and skips FILLER, which is why fixed text such as separators survives it as FILLER with a VALUE.

Practise: predict what each MOVE keeps

Predict what each MOVE keeps before you look at the answer. Every right answer is a line that one of this lesson’s runs printed, and every wrong one is a slip the lesson named:

Practice that adapts to you

Practise this lesson

17 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 17 Which entry of CUSTOMER-REC has no PICTURE clause?

    Choose one answer.

    Show the answer: Question 1 of 17

    Answer: CUST-NAME

    An entry with items under it is a group, and its size comes from them; every elementary entry has a PICTURE.

  2. Question 2 of 17 What does level 77 describe?

    Choose one answer.

    Show the answer: Question 2 of 17

    Answer: An elementary item that belongs to no record

    Level 77 is for an item on its own, such as a counter or a flag; many programs use 01-level items for the same purpose.

  3. Question 3 of 17 A record has three 05-level items, PIC X(6), PIC 9(5)V99 and PIC S9(3)V99 SIGN LEADING SEPARATE. How many bytes is it?

    Type a number.

    A hint

    Count each item as the previous lesson did, then add them up.

    Show the answer: Question 3 of 17

    Answer: 19 bytes

    6 bytes, 7 bytes (V takes none) and 6 bytes (five digits and the separate sign) make 19: a group is the total of the items under it.

  4. Question 4 of 17 What does MOVE 12345 TO an item of PIC 9(3) leave in the item?

    Choose one answer.

    Show the answer: Question 4 of 17

    Answer: 345

    Integer digits that do not fit are dropped on the left, so 12345 becomes 345, without a message at run time.

  5. Question 5 of 17 What does MOVE 2.678 TO an item of PIC 9V99 leave in the item?

    Choose one answer.

    Show the answer: Question 5 of 17

    Answer: 2.67

    A numeric MOVE truncates at both ends, never rounds: rounding is a phrase of the arithmetic statements, ROUNDED, which a later lesson covers.

  6. Question 6 of 17 Which build of numeric.cob reported line 24, MOVE INTEREST TO AMT?

    Choose one answer.

    Show the answer: Question 6 of 17

    Answer: Only the build with -Wpossible-truncate

    -Wall catches literals that do not fit; -Wpossible-truncate catches moves from wider variables, including some whose values would fit, such as line 22.

  7. Question 7 of 17 What does MOVE "PUNE" TO an item of PIC X(10) leave in the item?

    Choose one answer.

    Show the answer: Question 7 of 17

    Answer: PUNE followed by six spaces

    Text is left-aligned and padded on the right, so STATION printed [PUNE ].

  8. Question 8 of 17 BRANCH is PIC 9(4) VALUE 42. What does MOVE BRANCH TO BRANCH-TEXT leave in BRANCH-TEXT, PIC X(6)?

    Choose one answer.

    Show the answer: Question 8 of 17

    Answer: 0042 followed by two spaces

    A whole number moved into text arrives as its digits, 0042, then the text rules apply, so the session printed [0042 ].

  9. Question 9 of 17 Can JUSTIFIED RIGHT be given to a numeric-edited item, to push a printed amount to the right?

    Choose one answer.

    Show the answer: Question 9 of 17

    Answer: No; IBM does not allow it on numeric or edited items

    JUSTIFIED is for text. A number is placed by its decimal point, and an edited picture decides where its characters go.

  10. Question 10 of 17 How is a MOVE into a group item carried out?

    Choose one answer.

    Show the answer: Question 10 of 17

    Answer: As an alphanumeric MOVE of bytes, with no conversion

    A group MOVE copies, pads or cuts bytes from the left, so it is right only when the two layouts match byte for byte.

  11. Question 11 of 17 Why did ADD 1 TO QTY give 2001 when QTY held the digit 2 and three spaces?

    Choose one answer.

    Show the answer: Question 11 of 17

    Answer: The arithmetic read each space as a zero, so QTY counted as 2000

    Data that is not numeric in a numeric item gives wrong results without a message, unless the program is built with checks such as -debug.

  12. Question 12 of 17 You suspect quantities like the one in groupmove.cob in a nightly batch. Which build makes such data stop the program?

    Choose one answer.

    Show the answer: Question 12 of 17

    Answer: A build with -debug, which checks numeric data at run time

    Run-time checks catch what the compiler cannot see. Use -debug in test runs, and IS NUMERIC where data comes in.

  13. Question 13 of 17 IN-REC's QTY, PIC 9(2), holds 07. What is QTY OF OUT-REC, PIC 9(5), after MOVE CORRESPONDING IN-REC TO OUT-REC?

    Choose one answer.

    Show the answer: Question 13 of 17

    Answer: 00007

    MOVE CORRESPONDING is a list of elementary MOVEs, one for each name the two groups share, so every pair is converted properly.

  14. Question 14 of 17 Why did ambiguous.cob fail to compile?

    Choose one answer.

    Show the answer: Question 14 of 17

    Answer: QTY is used in two records, and one reference does not say which

    A name used twice is fine as long as each reference is qualified: QTY OF IN-REC and QTY IN OUT-REC.

  15. Question 15 of 17 What does plain INITIALIZE put into BALANCE, PIC S9(5)V99?

    Choose one answer.

    Show the answer: Question 15 of 17

    Answer: Zero

    INITIALIZE works by category: spaces for text, zeros for numbers, FILLER left alone.

  16. Question 16 of 17 What does BRANCH, PIC 9(4), hold after INITIALIZE CUST-REC REPLACING NUMERIC BY 9?

    Choose one answer.

    Show the answer: Question 16 of 17

    Answer: 0009

    REPLACING gives a category another starting value, moved like any MOVE: 9 became 0009 in BRANCH and 00009.00 in BALANCE.

  17. Question 17 of 17 Between two customers, a program clears CUST-REC with MOVE SPACES TO CUST-REC. What is the risk?

    Choose one answer.

    Show the answer: Question 17 of 17

    Answer: BALANCE and BRANCH hold spaces, which are not numbers

    A group is alphanumeric, so MOVE SPACES to it puts spaces into its numeric items. Clear records with INITIALIZE.

Find the truncations in your own code

Build a program of yours, or numeric.cob, with cobc -x -Wpossible-truncate prog.cob 2>&1 | grep warning. For each line it reports, write down the largest value the sending item can hold and what the receiver would keep of it.

Interview questions

Warm-up (fresher to 2–5 years): what does MOVE do with a value that is too long for the receiving item?

It drops what does not fit, without an error or a message. A numeric MOVE first lines up the decimal points, then drops integer digits on the left and decimal digits on the right, without rounding: 12345 into PIC 9(3) keeps 345, and 123.456 into PIC 9(3)V99 keeps 123.45. An unsigned receiver also loses the sign, so -42.5 becomes 042.50. An alphanumeric MOVE keeps the characters from the left and cuts the end, so CHENNAI CENTRAL in PIC X(10) becomes CHENNAI CE, unless the receiver is JUSTIFIED RIGHT, which keeps the right end instead. A group MOVE copies bytes the same way, with no conversion at all. GnuCOBOL’s -Wall warns only when the sender is a literal; -Wpossible-truncate also flags moves from wider variables.

Next come two questions of the kind a maintenance team meets: an interest figure that lost its millions, and a quantity that became 2001. Sketch your answer, then compare it with each first step:

Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.

  1. Fresher to mid

    Monthly interest totals match the ledger for every branch but the largest, whose report shows 567.89 against 1,234,567.89. The program builds cleanly with -Wall and nothing fails at run time. Where do you look, how do you prove the cause, and what stops it happening again?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    After a change to an input record, an order quantity of 12 is processed as 2001, and the nightly batch reports no errors. How do you find what happened, and how do you fix it and guard against it?

    Model answer, Pro. First step and follow-ups

Organisations named on this page

MySmartCoPilot has no tie to IBM or to the GnuCOBOL project. The MOVE and INITIALIZE rules come from IBM’s Language Reference and GnuCOBOL’s manual, both in References, and the outputs from this lesson’s GnuCOBOL 3.2 sessions.

Flashcards · 8 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. Which level numbers describe a record, and what is level 77 for?

    01 starts a record, and the entries inside it use higher numbers, 02 to 49. Level 77 is an elementary item that belongs to no record, such as a counter. Indentation means nothing to the compiler.

  2. How big is a group item?

    It has no PICTURE: its size is the total of the items under it. A record of fields of 6, 22, 4, 2 and 9 bytes takes 43 bytes.

  3. What does MOVE 1234567.89 to an item of PIC 9(3)V99 leave in it?

    567.89. A numeric MOVE lines up the two decimal points, then drops whatever falls outside the receiver at either end, without rounding and without a message.

  4. What happens to the sign of -42.5 moved into an unsigned PIC 9(3)V99?

    It is lost: an unsigned receiver takes the absolute value, so the item holds 042.50.

  5. Which GnuCOBOL flag warns about a MOVE from a wider variable?

    -Wpossible-truncate. It reads the PICTUREs, not the values, while -Wall only warns when the sender is a literal that does not fit.

  6. What does JUSTIFIED RIGHT change about an alphanumeric MOVE?

    The value is aligned at the right: a short one is padded with spaces on the left, a long one loses its leftmost characters. CHENNAI CENTRAL in PIC X(10) becomes AI CENTRAL.

  7. How can a group MOVE leave a quantity holding something that is not a number?

    A group counts as alphanumeric, so the MOVE copies bytes from the left and converts nothing. Each item inside gets whatever bytes land on it, digits or spaces.

  8. How does INITIALIZE differ from MOVE SPACES to a record?

    INITIALIZE gives text items spaces and numeric items zero, and leaves FILLER alone unless WITH FILLER is written. MOVE SPACES to the record puts spaces into its numbers as well.

Key takeaways

  • Level numbers make a record a tree: 01 for the record, higher numbers inside, 77 for an item on its own; a group has no PICTURE and its size is the total of its items, 43 bytes for CUSTOMER-REC.
  • A numeric MOVE lines up the decimal points and drops digits at either end without rounding or a message, so 1234567.89 became 567.89, and an unsigned receiver drops the sign.
  • An alphanumeric MOVE fills from the left, padding or cutting on the right; JUSTIFIED RIGHT pads and cuts on the left.
  • A group MOVE copies bytes and converts nothing, which is how a quantity came to hold 2 and three spaces and became 2001.
  • MOVE CORRESPONDING moves items of the same name, each with the full MOVE rules; OF or IN qualifies a name used twice, and INITIALIZE clears text to spaces and numbers to zeros, leaving FILLER alone.

Terms in this lesson

elementary item
A data item with no items under it, described by its own PICTURE clause, such as CUST-ID PIC X(6).
FILLER
The name of a data item that the program never refers to, such as a gap or a separator inside a record. INITIALIZE leaves FILLER items as they are unless told WITH FILLER.
group item
A data item made of the items under it. It has no PICTURE of its own, its size is the total of theirs, and a MOVE treats it as alphanumeric, byte for byte.
level number
The number that starts a data description entry and places it in a hierarchy: 01 for a record, 02 to 49 for the items inside it, 77 for an item of its own, 66 and 88 for special entries.
qualification
Making a name that is used twice unique by naming a group that contains it, with OF or IN, as in QTY OF OUT-REC.

References

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.