COBOL Module 4 – Conditions, loops and structured paragraphs
Conditions, IF and EVALUATE decision tables
Write COBOL relation, class and sign conditions, combine them with AND, OR and NOT without the abbreviation trap, and turn nested IFs into EVALUATE tables.
What you will learn
- Write relation, class, sign and condition-name conditions and combine them with AND, OR and NOT
- Rewrite abbreviated combined conditions in full, including the NOT trap
- Replace nested IF statements with EVALUATE TRUE and EVALUATE ... ALSO decision tables
- Predict which WHEN row of an EVALUATE runs when several rows match
Before you start
What you’ll learn · Before you start
What you will learn
- Write relation, class, sign and condition-name conditions and combine them with AND, OR and NOT
- Rewrite abbreviated combined conditions in full, including the NOT trap
- Replace nested IF statements with EVALUATE TRUE and EVALUATE ... ALSO decision tables
- Predict which WHEN row of an EVALUATE runs when several rows match
Before you start
On this page
A check meant to reject every transaction that is not a deposit or a withdrawal, IF TXN-TYPE NOT = “D” OR “W”, rejected all three records of a test file, the deposit and the withdrawal included. COBOL reads that short form as TXN-TYPE NOT = “D” OR TXN-TYPE NOT = “W”, and no single code can make both halves false.
Below you will write the four kinds of simple condition, combine them with AND, OR and NOT and read their short forms the way the compiler reads them, then turn nested IF statements into EVALUATE, up to a decision table of transaction rules whose rows are tried from the top.
A relation condition compares values, and text is padded with spaces
A relation condition: A condition that compares two operands with an operator such as =, < or NOT =. Numbers compare by value; text compares character by character, with the shorter operand padded with spaces. compares two operands with an operator: =, <, >, <=, >=
or NOT before one of them, or the same in words, such as IS GREATER THAN. IBM’s reference says it is true “if the
specified relation exists between the two operands”. What decides the result is the data on each side. Numbers compare
by their value, whatever their PICTURE. Text compares character by character from the left, and the shorter operand is
padded with spaces until both have the same length. This program tests four relations:
IDENTIFICATION DIVISION.
PROGRAM-ID. RELATION.
* Relation conditions: numbers compare by their value, text
* compares byte by byte, the shorter side padded with spaces.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 QTY PIC 9(3) VALUE 7.
01 LIMIT-QTY PIC 9(5)V99 VALUE 7.00.
01 CITY PIC X(10) VALUE "PUNE".
01 CITY-IN PIC X(10) VALUE " PUNE".
01 BRANCH-A PIC X(4) VALUE "BR10".
01 BRANCH-B PIC X(4) VALUE "BR9".
PROCEDURE DIVISION.
IF QTY = LIMIT-QTY
DISPLAY "QTY = LIMIT-QTY true"
ELSE
DISPLAY "QTY = LIMIT-QTY false"
END-IF
IF CITY = "PUNE"
DISPLAY "CITY = ""PUNE"" true"
ELSE
DISPLAY "CITY = ""PUNE"" false"
END-IF
IF CITY-IN = CITY
DISPLAY "CITY-IN = CITY true"
ELSE
DISPLAY "CITY-IN = CITY false"
END-IF
IF BRANCH-A < BRANCH-B
DISPLAY "BRANCH-A < BRANCH-B true"
ELSE
DISPLAY "BRANCH-A < BRANCH-B false"
END-IF
STOP RUN. from session import files, run
# Four relation conditions: two numbers of different sizes, a 10-byte item against a 4-byte literal, a value with a
# leading space, and two branch codes compared as text.
files("relation.cob")
run("""
cobc -x relation.cob && ./relation
""") Output
$ cobc -x relation.cob && ./relation QTY = LIMIT-QTY true CITY = "PUNE" true CITY-IN = CITY false BRANCH-A < BRANCH-B true
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 relation_session.py
QTY, PIC 9(3) holding 007, equals LIMIT-QTY, PIC 9(5)V99 holding 00007.00, because both are 7. CITY is 10 bytes and the literal “PUNE” is 4, so the literal was padded with six spaces for the comparison and CITY = “PUNE” is true. CITY-IN starts with a space, and “ PUNE” is not “PUNE”: a leading space is a character like any other. The last line surprises people: BR10 is less than BR9, because the comparison is decided at the third character, 1 against 9, and the digits are never read as a number.
| Operands | Compared as | In relation.cob |
|---|---|---|
| Two numeric items | Their values, with sign and decimal point | 007 = 00007.00 is true |
| Two alphanumeric items | Character by character, the shorter one padded with spaces | “PUNE” = PUNE in 10 bytes is true; BR10 < BR9 is true |
Which character comes first is set by the collating sequence: The order in which a program or a sort ranks characters when it compares them. ASCII puts digits before letters and EBCDIC puts letters before digits; COLLATING SEQUENCE names the one a SORT or MERGE uses. More in “SORT and MERGE: input and output procedures”. This run used ASCII, in which digits come before letters; a later lesson of this track shows the order on z/OS.
Codes that look like numbers
Wrong: comparing or sorting branch codes such as BR9 and BR10 as text and expecting the order of their numbers. Right: give the codes a fixed width with leading zeros, BR09 and BR10, so the text order and the number order agree, or move the number part into a PIC 9 item and compare that.
Class and sign conditions test what a field holds
Input from a file or a screen often arrives as text, PIC X, before the program can trust it as a number. A class condition: A test of which characters an item holds: NUMERIC for digits only, ALPHABETIC for letters and spaces, and ALPHABETIC-UPPER or ALPHABETIC-LOWER for one case of letters and spaces. tests which characters an item holds. IBM’s reference defines NUMERIC as “the characters 0 through 9, with or without an operational sign”, ALPHABETIC as the letters A to Z, a to z “and the space”, and ALPHABETIC-UPPER and ALPHABETIC-LOWER as one case of letters, the space included again. A sign condition: A test of a numeric value against zero: POSITIVE, NEGATIVE or ZERO. Zero is neither positive nor negative. tests a number against zero: POSITIVE, NEGATIVE or ZERO. This program puts six five-byte inputs through the class conditions and three balances through the sign conditions:
IDENTIFICATION DIVISION.
PROGRAM-ID. CLASSSIGN.
* Class conditions ask which characters a field holds; sign
* conditions ask whether a number is above or below zero.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 FIELD-IN PIC X(5).
01 BALANCE PIC S9(5)V99.
01 ANSWERS.
05 IS-NUM PIC X(9).
05 IS-ALPHA PIC X(12).
05 IS-UPPER PIC X(8).
05 IS-LOWER PIC X(5).
PROCEDURE DIVISION.
DISPLAY "input NUMERIC ALPHABETIC -UPPER -LOWER"
MOVE "01250" TO FIELD-IN
PERFORM SHOW-CLASS
MOVE "1250 " TO FIELD-IN
PERFORM SHOW-CLASS
MOVE "12A50" TO FIELD-IN
PERFORM SHOW-CLASS
MOVE "PUNE " TO FIELD-IN
PERFORM SHOW-CLASS
MOVE "pune " TO FIELD-IN
PERFORM SHOW-CLASS
MOVE SPACES TO FIELD-IN
PERFORM SHOW-CLASS
MOVE -35.50 TO BALANCE
PERFORM SHOW-SIGN
MOVE ZERO TO BALANCE
PERFORM SHOW-SIGN
MOVE 120 TO BALANCE
PERFORM SHOW-SIGN
STOP RUN.
SHOW-CLASS.
MOVE "no" TO IS-NUM IS-ALPHA IS-UPPER IS-LOWER
IF FIELD-IN IS NUMERIC MOVE "yes" TO IS-NUM END-IF
IF FIELD-IN IS ALPHABETIC MOVE "yes" TO IS-ALPHA END-IF
IF FIELD-IN IS ALPHABETIC-UPPER MOVE "yes" TO IS-UPPER END-IF
IF FIELD-IN IS ALPHABETIC-LOWER MOVE "yes" TO IS-LOWER END-IF
DISPLAY "[" FIELD-IN "] " ANSWERS.
SHOW-SIGN.
EVALUATE TRUE
WHEN BALANCE IS POSITIVE
DISPLAY BALANCE " is POSITIVE"
WHEN BALANCE IS NEGATIVE
DISPLAY BALANCE " is NEGATIVE"
WHEN BALANCE IS ZERO
DISPLAY BALANCE " is ZERO"
END-EVALUATE. from session import files, run
# Six five-byte inputs through the class conditions, then three balances through the sign conditions.
files("classsign.cob")
run("""
cobc -x classsign.cob && ./classsign
""") Output
$ cobc -x classsign.cob && ./classsign input NUMERIC ALPHABETIC -UPPER -LOWER [01250] yes no no no [1250 ] no no no no [12A50] no no no no [PUNE ] no yes yes no [pune ] no yes no yes [ ] no yes yes yes -00035.50 is NEGATIVE +00000.00 is ZERO +00120.00 is POSITIVE
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 classsign_session.py
Only 01250 is NUMERIC. 1250 with a trailing space is not, and 1250.75 would not be either, because a space and a decimal point are not digits; a later lesson converts such text with FUNCTION NUMVAL. The row of five spaces is the trap on the other side: it is ALPHABETIC, ALPHABETIC-UPPER and ALPHABETIC-LOWER at once, so a blank name passes IF NAME-IN IS ALPHABETIC, and a test for SPACES has to come first. The balances show that zero is its own case: -35.50 is NEGATIVE, 0 is ZERO and 120.00 is POSITIVE. SHOW-SIGN uses EVALUATE TRUE, which runs the first WHEN whose condition is true, as a section below shows.
condition name: A level-88 entry that names a value, a list of values or a THRU range of the item above it, so that IF ACCT-OPEN tests that item against them. More in “Condition names (88), REDEFINES and RENAMES”, the level-88 entries of the lesson on condition names, are the fourth kind of simple condition: IF ACCT-OPEN tests ACCT-STATUS against the values of ACCT-OPEN. All four kinds combine with AND, OR and NOT in the same way.
Key idea
Test what a field holds before you use it: NUMERIC accepts digits only, so a point or a space fails it, and the ALPHABETIC tests accept spaces, so a blank field passes them.
Checkpoint
Check: Relation conditions and padded text · Class and sign conditions
AND is evaluated before OR, so brackets decide the meaning
A combined condition: Conditions joined with AND and OR. Without brackets AND is evaluated before OR, so A OR B AND C means A OR (B AND C). joins conditions with AND and OR. The rule of this program is “a deposit or a withdrawal over 50000.00 gets a second check”, and its first IF writes the rule the way it is said:
IDENTIFICATION DIVISION.
PROGRAM-ID. COMBINED.
* The rule: a deposit or a withdrawal over 50000.00 gets a
* second check. AND is evaluated before OR, so the brackets
* decide what the condition means.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 TXN-TYPE PIC X.
01 AMOUNT PIC 9(7)V99.
01 AMOUNT-OUT PIC Z(6)9.99.
PROCEDURE DIVISION.
MOVE "D" TO TXN-TYPE
MOVE 200 TO AMOUNT
PERFORM CHECK-TXN
MOVE "W" TO TXN-TYPE
PERFORM CHECK-TXN
MOVE 60000 TO AMOUNT
PERFORM CHECK-TXN
STOP RUN.
CHECK-TXN.
MOVE AMOUNT TO AMOUNT-OUT
DISPLAY TXN-TYPE " " AMOUNT-OUT WITH NO ADVANCING
IF TXN-TYPE = "D" OR TXN-TYPE = "W" AND AMOUNT > 50000
DISPLAY " no brackets: check" WITH NO ADVANCING
ELSE
DISPLAY " no brackets: pass " WITH NO ADVANCING
END-IF
IF (TXN-TYPE = "D" OR TXN-TYPE = "W") AND AMOUNT > 50000
DISPLAY " brackets: check"
ELSE
DISPLAY " brackets: pass"
END-IF. Without brackets, AND is evaluated before OR. IBM’s reference shows NOT A IS GREATER THAN B OR A + B IS EQUAL TO C AND D IS POSITIVE evaluated as if it were written (NOT (A IS GREATER THAN B)) OR (((A + B) IS EQUAL TO C) AND (D IS POSITIVE)). So the first IF means “a deposit, or a withdrawal over 50000.00”: the amount limit belongs to the withdrawal alone. Run both versions on three transactions:
from session import files, run
# Three transactions through the same rule written without and with brackets, built with -Wall, which prints no
# warning for either IF.
files("combined.cob")
run("""
cobc -x -Wall combined.cob && ./combined
""") Output
$ cobc -x -Wall combined.cob && ./combined D 200.00 no brackets: check brackets: pass W 200.00 no brackets: pass brackets: pass W 60000.00 no brackets: check brackets: check
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 combined_session.py
The deposit of 200.00 went for a check without brackets and passed with them; the two withdrawals came out the same both ways. Step through how each IF reaches its answer for the deposit:
The drawing shows the whole scene; its 8 steps are listed below.
Every step as text
- The record. CHECK-TXN reads a deposit, TXN-TYPE D, of 200.00. The rule says only a deposit or a withdrawal over 50000.00 needs a second check, so this one should pass.
- How the words group. Without brackets, AND is evaluated before OR, so the condition means TXN-TYPE = "D" OR (TXN-TYPE = "W" AND AMOUNT > 50000). The amount limit belongs to withdrawals only.
- Left to right. The evaluation starts at the left. TXN-TYPE = "D" is true for this record.
- The answer is known. One true side makes an OR true, so the result is known and the AND part is never evaluated. IBM's reference says evaluation stops as soon as the truth value is determined.
- The wrong result. The IF is true and the deposit of 200.00 goes for a second check. The session printed no brackets, check for this record.
- The brackets first. The second IF puts (TXN-TYPE = "D" OR TXN-TYPE = "W") in brackets, so the type test is evaluated as one condition first, and it is true for D.
- Then the amount. AND needs both sides, so AMOUNT > 50000 is tested too. It is false for 200.00, which makes the whole condition false.
- The right result. The deposit passes, as the rule says. Only W 60000.00 went for a check in both versions, because its amount is over the limit.
The evaluation also stops early. Once one side of an OR is true, the other side is not evaluated, and once one side of an AND is false, the rest is skipped: IBM’s reference says each level “terminates as soon as a truth value for it is determined”. The -Wall build printed no warning for either IF, because both are valid COBOL. Only the brackets say which meaning you want.
Brackets wherever AND meets OR
Write brackets in every condition that mixes AND and OR, even where the default grouping is what you mean, so that the next reader never has to know the rule.
A short condition repeats the subject and the operator, NOT included
COBOL lets a combined condition leave out what repeats. In an abbreviated combined condition: A combined condition that leaves out a repeated subject, or the subject and its operator: X = "D" OR "W" means X = "D" OR X = "W". A NOT written just before the operator is repeated with it., TXN-TYPE = “D” OR “W” means TXN-TYPE = “D” OR TXN-TYPE = “W”. IBM’s rules say the missing subject is “the last stated subject” and the missing operator “the last stated relational operator”. The trap is that a NOT written just before = belongs to that operator, so it is repeated too. This program writes the check of the opening five ways and runs it for D, W and X:
IDENTIFICATION DIVISION.
PROGRAM-ID. ABBREV.
* Five ways to write "reject anything but D (deposit) and W
* (withdrawal)". An abbreviated condition repeats the subject
* and the operator, NOT included.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 TXN-TYPE PIC X.
88 TXN-VALID VALUE "D" "W".
01 RESULTS.
05 SHORT-FORM PIC X(12).
05 AND-FORM PIC X(10).
05 BRACKETS PIC X(10).
05 NOT-FIRST PIC X(11).
05 LEVEL-88 PIC X(7).
PROCEDURE DIVISION.
DISPLAY "code short form AND form brackets NOT first"
" 88-level"
MOVE "D" TO TXN-TYPE
PERFORM CHECK-TYPE
MOVE "W" TO TXN-TYPE
PERFORM CHECK-TYPE
MOVE "X" TO TXN-TYPE
PERFORM CHECK-TYPE
STOP RUN.
CHECK-TYPE.
MOVE "accepts" TO SHORT-FORM AND-FORM BRACKETS NOT-FIRST
LEVEL-88
IF TXN-TYPE NOT = "D" OR "W"
MOVE "rejects" TO SHORT-FORM
END-IF
IF TXN-TYPE NOT = "D" AND NOT = "W"
MOVE "rejects" TO AND-FORM
END-IF
IF NOT (TXN-TYPE = "D" OR "W")
MOVE "rejects" TO BRACKETS
END-IF
IF NOT TXN-TYPE = "D" OR "W"
MOVE "rejects" TO NOT-FIRST
END-IF
IF NOT TXN-VALID
MOVE "rejects" TO LEVEL-88
END-IF
DISPLAY TXN-TYPE " " RESULTS. from session import files, run
# The codes D, W and X through five ways of writing "reject anything but D and W", built with -Wall, which prints no
# warning for any of them.
files("abbrev.cob")
run("""
cobc -x -Wall abbrev.cob && ./abbrev
""") Output
$ cobc -x -Wall abbrev.cob && ./abbrev code short form AND form brackets NOT first 88-level D rejects accepts accepts accepts accepts W rejects accepts accepts rejects accepts X rejects rejects rejects rejects rejects
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 abbrev_session.py
| Column | The IF in CHECK-TYPE | Read in full | Rejected |
|---|---|---|---|
| short form | TXN-TYPE NOT = "D" OR "W" |
NOT = “D” OR NOT = “W” | D, W and X |
| AND form | TXN-TYPE NOT = "D" AND NOT = "W" |
NOT = “D” AND NOT = “W” | X |
| brackets | NOT (TXN-TYPE = "D" OR "W") |
NOT (= “D” OR = “W”) | X |
| NOT first | NOT TXN-TYPE = "D" OR "W" |
(NOT = “D”) OR = “W” | W and X |
| 88-level | NOT TXN-VALID |
NOT (= “D” OR = “W”) | X |
The short form rejected all three codes, as in the opening: D is not W and W is not D, so one side of its OR is always true. A NOT before the subject is the opposite slip. IBM’s rules make it a logical NOT that negates “only the relation condition immediately following” it, so NOT TXN-TYPE = “D” OR “W” rejected W, a valid withdrawal. The AND form, the brackets and the condition name each reject X alone.
NOT = with OR
Wrong: IF TXN-TYPE NOT = “D” OR “W”, which is true for every code. Right: IF NOT TXN-VALID, with 88 TXN-VALID VALUE “D” “W” under TXN-TYPE, or IF TXN-TYPE NOT = “D” AND NOT = “W”. The -Wall build warned about neither version.
Of the three right versions, the condition name is the one to keep. The valid codes are listed once, in the DATA DIVISION, so the test reads as its meaning and a new code is one more value in one place.
Key idea
Read a short condition by writing the subject and the operator back in front of every value; a NOT just before the operator is repeated with them, and a NOT before the subject negates only the first comparison.
Checkpoint
Check: AND before OR, and brackets · Abbreviated conditions and the NOT trap
EVALUATE TRUE runs the first WHEN that is true
A rule with several bands is often written as IF inside ELSE inside IF, and every new band adds a level of nesting. This program works out the fee of a transfer by a teaching rule, 0.00 for zero, 2.50 up to 10000.00, 5.00 up to 100000.00 and 15.00 above that, three ways: nested IF, EVALUATE TRUE, and EVALUATE TRUE with its first rows in the wrong order:
IDENTIFICATION DIVISION.
PROGRAM-ID. BANDS.
* The fee of a transfer by amount band (a teaching rule), three
* ways: nested IF, EVALUATE TRUE, and EVALUATE TRUE with two
* WHEN rows in the wrong order.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 AMOUNT PIC 9(7)V99.
01 FEE PIC 99V99.
01 AMOUNT-OUT PIC Z(6)9.99.
01 FEE-OUT PIC Z9.99.
PROCEDURE DIVISION.
DISPLAY " amount IF EVALUATE wrong order"
MOVE 0 TO AMOUNT
PERFORM SHOW-FEES
MOVE 5000 TO AMOUNT
PERFORM SHOW-FEES
MOVE 75000 TO AMOUNT
PERFORM SHOW-FEES
MOVE 250000 TO AMOUNT
PERFORM SHOW-FEES
STOP RUN.
SHOW-FEES.
MOVE AMOUNT TO AMOUNT-OUT
DISPLAY AMOUNT-OUT WITH NO ADVANCING
PERFORM FEE-BY-IF
PERFORM FEE-BY-EVALUATE
PERFORM FEE-WRONG-ORDER
DISPLAY SPACE.
FEE-BY-IF.
IF AMOUNT = ZERO
MOVE ZERO TO FEE
ELSE
IF AMOUNT <= 10000
MOVE 2.50 TO FEE
ELSE
IF AMOUNT <= 100000
MOVE 5.00 TO FEE
ELSE
MOVE 15.00 TO FEE
END-IF
END-IF
END-IF
MOVE FEE TO FEE-OUT
DISPLAY " " FEE-OUT WITH NO ADVANCING.
FEE-BY-EVALUATE.
EVALUATE TRUE
WHEN AMOUNT = ZERO
MOVE ZERO TO FEE
WHEN AMOUNT <= 10000
MOVE 2.50 TO FEE
WHEN AMOUNT <= 100000
MOVE 5.00 TO FEE
WHEN OTHER
MOVE 15.00 TO FEE
END-EVALUATE
MOVE FEE TO FEE-OUT
DISPLAY " " FEE-OUT WITH NO ADVANCING.
FEE-WRONG-ORDER.
EVALUATE TRUE
WHEN AMOUNT <= 100000
MOVE 5.00 TO FEE
WHEN AMOUNT = ZERO
MOVE ZERO TO FEE
WHEN AMOUNT <= 10000
MOVE 2.50 TO FEE
WHEN OTHER
MOVE 15.00 TO FEE
END-EVALUATE
MOVE FEE TO FEE-OUT
DISPLAY " " FEE-OUT WITH NO ADVANCING. from session import files, run
# Four amounts through the same fee rule written three ways.
files("bands.cob")
run("""
cobc -x bands.cob && ./bands
""") Output
$ cobc -x bands.cob && ./bands
amount IF EVALUATE wrong order
0.00 0.00 0.00 5.00
5000.00 2.50 2.50 5.00
75000.00 5.00 5.00 5.00
250000.00 15.00 15.00 15.00
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 bands_session.py
FEE-BY-IF and FEE-BY-EVALUATE agree on all four amounts, and EVALUATE TRUE reads like the rule: one WHEN per band, from the top. It compares the subject TRUE with each WHEN condition, in the order the rows are written: IBM’s reference says the WHEN phrases are tested “in the order of their appearance in the source text”, and the statements of the first one selected run. WHEN OTHER runs when no row is selected; without it, the EVALUATE does nothing and the program continues after END-EVALUATE. Step through the rows for 75000.00, then for 5000.00 and 0.00:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- The first row. For 75000.00, EVALUATE TRUE tests the first WHEN row. AMOUNT = ZERO is false, so it moves to the next row.
- The second row. AMOUNT <= 10000 is false too. Nothing has run yet.
- A true row. AMOUNT <= 100000 is true, so MOVE 5.00 TO FEE runs. This is the first true WHEN, and it is the only one that runs.
- The rest is skipped. Control goes to the end of the EVALUATE. WHEN OTHER is never tested, and the fee is 5.00, as the session printed.
- A smaller amount. For 5000.00 the second row is the first true one, AMOUNT <= 10000, so the fee is 2.50. The third row is true as well, but it is never reached.
- Zero. For 0.00 the first row is already true, so the fee is 0.00. In FEE-WRONG-ORDER the row AMOUNT <= 100000 comes first and catches 0.00 and 5000.00, which is why that column printed 5.00 for both.
The wrong-order column shows what a wide row does at the top. AMOUNT <= 100000 is true for 0.00 and 5000.00 as well,
so it caught both, and the rows written for them never ran. Order the rows from the narrowest condition to the widest,
or make the conditions exclusive, for example with EVALUATE AMOUNT and ranges such as WHEN 0.01 THRU 10000.
Key idea
In EVALUATE TRUE the first true WHEN runs and every row after it is skipped, so the order of the rows is part of the rule.
EVALUATE with ALSO turns a decision table into code
Some rules depend on several things at once. A decision table: A table of rules in which each row lists the values of several conditions and the action they lead to. EVALUATE with ALSO writes one as code, and its first matching row decides. writes them as rows, with the conditions in columns and the action at the end. These teaching rules decide what happens to a transaction from its type, D or W, the customer’s Know your customer: the checks of a customer's identity that a bank completes before it allows some transactions. status, V for verified or P for pending, and its amount:
| Row | TXN-TYPE | KYC-STATUS | Amount | Action |
|---|---|---|---|---|
| 1 | D | P | any | hold |
| 2 | D | V | any | post |
| 3 | W | V | more than the balance | reject |
| 4 | W | V | more than 50000 | refer |
| 5 | W | V | any | post |
| other | any | any | any | reject |
EVALUATE writes the table as it stands. The selection subject: An operand written after EVALUATE, or after an ALSO there, such as TRUE or a data item. Each entry of a WHEN row is compared with the subject in the same position. come after EVALUATE, separated by ALSO: TXN-TYPE ALSO KYC-STATUS ALSO TRUE. Each WHEN row has one entry for each subject, in the same order: a value for the first two and a condition for TRUE. ANY matches whatever its subject holds; IBM’s reference says “the comparison is always satisfied”. A row is selected only when every one of its entries matches, and the first such row decides:
IDENTIFICATION DIVISION.
PROGRAM-ID. TXNRULES.
* A decision table as EVALUATE ... ALSO: the type of a
* transaction, the customer's KYC status (V verified, P
* pending) and a condition on the amount. The first WHEN row
* whose three entries all match decides; the rules are a
* teaching example.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 CASE-IN.
05 TXN-TYPE PIC X.
05 KYC-STATUS PIC X.
05 AMOUNT PIC 9(5).
05 BALANCE PIC 9(5).
01 ACTION PIC X(6).
PROCEDURE DIVISION.
MOVE "DP0500090000" TO CASE-IN
PERFORM DECIDE
MOVE "DV0500090000" TO CASE-IN
PERFORM DECIDE
MOVE "WV8000060000" TO CASE-IN
PERFORM DECIDE
MOVE "WV7000090000" TO CASE-IN
PERFORM DECIDE
MOVE "WV0200090000" TO CASE-IN
PERFORM DECIDE
MOVE "WP0200090000" TO CASE-IN
PERFORM DECIDE
MOVE "TV0200090000" TO CASE-IN
PERFORM DECIDE
STOP RUN.
DECIDE.
EVALUATE TXN-TYPE ALSO KYC-STATUS ALSO TRUE
WHEN "D" ALSO "P" ALSO ANY
MOVE "hold" TO ACTION
WHEN "D" ALSO "V" ALSO ANY
MOVE "post" TO ACTION
WHEN "W" ALSO "V" ALSO AMOUNT > BALANCE
MOVE "reject" TO ACTION
WHEN "W" ALSO "V" ALSO AMOUNT > 50000
MOVE "refer" TO ACTION
WHEN "W" ALSO "V" ALSO ANY
MOVE "post" TO ACTION
WHEN OTHER
MOVE "reject" TO ACTION
END-EVALUATE
DISPLAY TXN-TYPE " " KYC-STATUS " amount " AMOUNT
" balance " BALANCE " -> " ACTION. from session import files, run
# Seven transactions through one EVALUATE ... ALSO decision table.
files("txnrules.cob")
run("""
cobc -x txnrules.cob && ./txnrules
""") Output
$ cobc -x txnrules.cob && ./txnrules D P amount 05000 balance 90000 -> hold D V amount 05000 balance 90000 -> post W V amount 80000 balance 60000 -> reject W V amount 70000 balance 90000 -> refer W V amount 02000 balance 90000 -> post W P amount 02000 balance 90000 -> reject T V amount 02000 balance 90000 -> reject
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 txnrules_session.py
The first transaction, a deposit while KYC is pending, was held. Look at the third: a withdrawal of 80000 from a balance of 60000 matches row 3, and it matches row 4 as well, since 80000 is over 50000. It was rejected, the action of row 3. Step through it:
The drawing shows the whole scene; its 5 steps are listed below.
Every step as text
- The transaction. The third transaction is a withdrawal, W, by a verified customer, V, of 80000 from a balance of 60000. EVALUATE compares the three subjects, TXN-TYPE, KYC-STATUS and TRUE, with each row in turn.
- Row 1. The first entry of row 1 is D, and TXN-TYPE is W, so row 1 cannot match, whatever its other two entries say.
- Row 2. Row 2 also starts with D, so it does not match either.
- Row 3 matches. Row 3 is W, V and AMOUNT > BALANCE. W matches, V matches, and 80000 > 60000 is true, so all three entries match and MOVE "reject" TO ACTION runs.
- Row 4 is never tried. AMOUNT > 50000 is true for this transaction too, so row 4 would match as well, but the first matching row decides. Control goes to the end of the EVALUATE, and the session printed reject, not refer.
The order of overlapping rows is a business decision, here “not enough money” before “large amount”, and the code states it by position. The withdrawal with KYC pending matched no row, and neither did T, an unknown type, so both fell to WHEN OTHER and were rejected. The rows must also have the right shape: IBM’s reference requires as many entries in each WHEN as there are subjects.
Key idea
EVALUATE with ALSO checks each entry of a row against its subject and runs the first row whose entries all match, so rows that overlap must be ordered on purpose.
Checkpoint
Check: EVALUATE TRUE and the order of its rows · Decision tables with EVALUATE and ALSO
Practise: predict the decision
Every question below is about a condition or a WHEN row of the six programs above. Work out the result on paper first and check it against the recorded session; the set opens with your weakest idea, and each choice carries its own reason:
Practice that adapts to you
Practise this lesson
19 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Then change the programs and run them where GnuCOBOL is installed:
Reorder two rows, then add one
Swap rows 3 and 4 in DECIDE of txnrules.cob, predict which of the seven lines will change, then build and run it with
cobc -x txnrules.cob && ./txnrules. Next, add a row that refers a verified deposit over 50000, and decide where it
must go for that deposit to be referred rather than posted.
A second change removes the nesting from the fee rule altogether. Rewrite FEE-BY-IF of bands.cob as EVALUATE AMOUNT with WHEN ZERO, WHEN 0.01 THRU 10000, WHEN 10000.01 THRU 100000 and WHEN OTHER: a range in a WHEN row matches when the amount is any value inside it, ends included, so the rows no longer overlap and their order stops mattering. Run it and check that its column agrees with the IF column for all four amounts.
Interview questions
Warm-up (fresher to 2–5 years): what does IF TXN-TYPE NOT = “D” OR “W” test, and how do you write what was meant?
It tests TXN-TYPE NOT = “D” OR TXN-TYPE NOT = “W”, because an abbreviated condition repeats its subject and its relational operator, and a NOT written just before = is part of the operator. Every code makes one side of that OR true: D is not W, and W is not D. So the condition is true for every value, and a check meant to reject unknown codes rejects deposits and withdrawals too, without a warning from cobc -Wall. What was meant is “neither D nor W”: TXN-TYPE NOT = “D” AND NOT = “W”, or NOT (TXN-TYPE = “D” OR “W”). The clearest form is a condition name, 88 TXN-VALID VALUE “D” “W” under TXN-TYPE, tested with IF NOT TXN-VALID, because the valid codes then live in one place.
Two harder questions follow: a decision table that changed its answers after a release, and input checks that let bad data through. Decide your first step before you open each one:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Mid to senior
A release added one WHEN row to an EVALUATE ... ALSO table of transaction rules. Since then, some withdrawals larger than the balance are referred for a second check instead of being rejected, although no row about them changed. Why, and how do you test such a table so that it cannot happen again?
-
Fresher to mid
A validation paragraph accepts an amount, PIC X(9), only IF AMOUNT-IN IS NUMERIC, and a name only IF NAME-IN IS ALPHABETIC. Clerks report that 1250.75 is refused, and the error log shows records with blank names posted. What is wrong with each check, and what do you replace them with?
Organisations named on this page
IBM and the GnuCOBOL project appear here only as sources and are not connected with MySmartCoPilot: the rules for conditions and EVALUATE are quoted from IBM’s documentation, and every output is a GnuCOBOL 3.2 run.
Flashcards · 8 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
How does COBOL compare two pieces of text of different lengths?
Character by character from the left, the shorter one padded with spaces. "PUNE" equals PUNE in a 10-byte item, while " PUNE" with a leading space does not.
-
Why does BR10 sort before BR9?
Text is compared character by character, and the third character decides: 1 comes before 9. Give codes a fixed width with leading zeros, BR09, or compare their number part as a number.
-
Does a field of five spaces pass IS ALPHABETIC?
Yes, all three ALPHABETIC tests accept the space, so a blank name passes them; test for SPACES first. NUMERIC accepts digits only, so a space or a point fails it.
-
In A OR B AND C, which is evaluated first?
AND, so it means A OR (B AND C). Write brackets wherever AND and OR meet, so no reader has to know the rule.
-
What does IF TXN-TYPE NOT = "D" OR "W" test?
TXN-TYPE NOT = "D" OR TXN-TYPE NOT = "W", because the subject and the operator, NOT included, repeat. That is true for every code. Write NOT = "D" AND NOT = "W", or IF NOT TXN-VALID.
-
In NOT TXN-TYPE = "D" OR "W", what does the NOT negate?
Only the comparison right after it, so the condition reads (NOT = "D") OR = "W" and rejects the valid code W.
-
Which WHEN of an EVALUATE TRUE runs?
The first one, in source order, whose condition is true; every row after it is skipped. WHEN OTHER runs when none matched, and without it the EVALUATE does nothing.
-
How does EVALUATE ... ALSO write a decision table?
The subjects after EVALUATE, separated by ALSO, are the columns, and each WHEN row has one entry for each, ANY matching anything. The first row whose entries all match decides, so rows that overlap are ordered on purpose.
Key takeaways
- Numbers compare by value and text compares character by character, the shorter side padded with spaces, so “PUNE” equals PUNE in a 10-byte item and BR10 sorts before BR9.
- NUMERIC accepts digits only, so a point or a space fails it, while the ALPHABETIC tests accept spaces, so a blank field passes them; zero is neither POSITIVE nor NEGATIVE.
- AND is evaluated before OR and evaluation stops once the result is known, so a condition that mixes them needs brackets to say what it means.
- An abbreviated condition repeats its subject and operator, a NOT before = included, which makes NOT = “D” OR “W” true for every code; write the AND form, brackets or a condition name instead.
- EVALUATE TRUE and EVALUATE with ALSO run the first WHEN row that matches and skip the rest, so the order of the rows is part of the rule, and WHEN OTHER takes whatever no row matched.
Terms in this lesson
- abbreviated combined condition
- A combined condition that leaves out a repeated subject, or the subject and its operator: X = "D" OR "W" means X = "D" OR X = "W". A NOT written just before the operator is repeated with it.
- class condition
- A test of which characters an item holds: NUMERIC for digits only, ALPHABETIC for letters and spaces, and ALPHABETIC-UPPER or ALPHABETIC-LOWER for one case of letters and spaces.
- combined condition
- Conditions joined with AND and OR. Without brackets AND is evaluated before OR, so A OR B AND C means A OR (B AND C).
- decision table
- A table of rules in which each row lists the values of several conditions and the action they lead to. EVALUATE with ALSO writes one as code, and its first matching row decides.
- relation condition
- A condition that compares two operands with an operator such as =, < or NOT =. Numbers compare by value; text compares character by character, with the shorter operand padded with spaces.
- selection subject
- An operand written after EVALUATE, or after an ALSO there, such as TRUE or a data item. Each entry of a WHEN row is compared with the subject in the same position.
- sign condition
- A test of a numeric value against zero: POSITIVE, NEGATIVE or ZERO. Zero is neither positive nor negative.
References
- ISO/IEC 1989:2023 Programming language COBOL (International Organization for Standardization)
- Enterprise COBOL for z/OS 6.5: Relation conditions (IBM)
- Enterprise COBOL for z/OS 6.5: Class condition (IBM)
- Enterprise COBOL for z/OS 6.5: Sign condition (IBM)
- Enterprise COBOL for z/OS 6.5: Combined conditions (IBM)
- Enterprise COBOL for z/OS 6.5: Abbreviated combined relation conditions (IBM)
- Enterprise COBOL for z/OS 6.5: IF statement (IBM)
- Enterprise COBOL for z/OS 6.5: EVALUATE statement (IBM)
- Enterprise COBOL for z/OS 6.5: Comparing selection subjects and objects (IBM)
- Enterprise COBOL for z/OS 6.5: Executing the EVALUATE statement (IBM)
Report a problem with this lesson
Kept only in this browser. Your Learn progress