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.
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:
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, 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:
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
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:
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:
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:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- The value sent. The sending item INTEREST holds 1234567.89, seven digits before its implied decimal point and two after it.
- 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.
- 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.
- 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.
- 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
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:
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. 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:
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. 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:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- 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.
- 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.
- 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.
- 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.
- 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
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:
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:
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:
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. 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
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.
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.
-
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?
-
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?
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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.
-
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
- Enterprise COBOL for z/OS 6.5: Level-numbers (IBM)
- Enterprise COBOL for z/OS 6.5: VALUE clause (IBM)
- Enterprise COBOL for z/OS 6.5: MOVE statement (IBM)
- Enterprise COBOL for z/OS 6.5: Elementary move rules (IBM)
- Enterprise COBOL for z/OS 6.5: Group moves (IBM)
- Enterprise COBOL for z/OS 6.5: JUSTIFIED clause (IBM)
- Enterprise COBOL for z/OS 6.5: INITIALIZE statement rules (IBM)
- GnuCOBOL manual: compiler options (GnuCOBOL project (GNU))
Report a problem with this lesson
Kept only in this browser. Your Learn progress