17 min read

Every meaningful business application makes decisions. Should this loan be approved? Is this transaction fraudulent? Does this employee qualify for a raise? At the heart of every such question lies conditional logic -- the ability of a program to...

Chapter 7: Conditional Logic -- IF, EVALUATE, and Condition Names

Introduction: Where Business Rules Come to Life

Every meaningful business application makes decisions. Should this loan be approved? Is this transaction fraudulent? Does this employee qualify for a raise? At the heart of every such question lies conditional logic -- the ability of a program to examine data and choose a course of action based on what it finds.

COBOL was designed from its earliest days to express business rules clearly, and its conditional logic facilities reflect that mission. Where other languages offer terse symbols and compact syntax, COBOL provides readable, English-like constructs that allow a business analyst to look at the code and understand the rule being implemented. This is not an accident; it is the fundamental design philosophy that has kept COBOL relevant for over six decades.

In this chapter, you will master three primary mechanisms for conditional logic in COBOL:

  1. The IF statement -- the workhorse of decision-making, supporting simple, nested, and compound conditions
  2. The EVALUATE statement -- COBOL's powerful multi-way branching construct, far more capable than the SWITCH/CASE of other languages
  3. Condition names (88-levels) -- a uniquely COBOL feature that transforms cryptic codes and magic numbers into self-documenting, readable conditions

By the end of this chapter, you will be able to implement complex business rule engines, validation systems, and decision trees using idiomatic COBOL that is both correct and maintainable.


7.1 The IF Statement

The IF statement is the most fundamental decision-making construct in COBOL. It evaluates a condition and executes one set of statements if the condition is true, and optionally a different set if the condition is false.

7.1.1 Basic Syntax

The general form of the IF statement is:

       IF condition
           statement-1
           statement-2
       ELSE
           statement-3
           statement-4
       END-IF

The ELSE clause is optional. When omitted, the IF statement simply executes the statements when the condition is true and skips them otherwise:

       IF WS-ACCOUNT-BALANCE < ZERO
           DISPLAY "Account is overdrawn!"
       END-IF

When the ELSE clause is present, the program takes one path or the other -- never both, never neither:

       IF WS-EMPLOYEE-AGE >= 65
           DISPLAY "Eligible for retirement benefits."
           PERFORM CALCULATE-PENSION
       ELSE
           DISPLAY "Not yet eligible for retirement."
           PERFORM CALCULATE-CURRENT-BENEFITS
       END-IF

Multiple statements can appear in both the IF and ELSE branches. There is no need for braces, brackets, or BEGIN/END blocks -- COBOL uses the scope terminator END-IF to mark the end of the entire construct.

7.1.2 The Importance of END-IF (Scope Terminators)

Prior to the COBOL-85 standard, the IF statement was terminated by a period. This led to one of the most infamous sources of bugs in COBOL programming history. Consider this pre-85 style code:

      * PRE-COBOL-85 STYLE (DO NOT USE)
           IF WS-AMOUNT > 1000
               PERFORM PROCESS-LARGE-ORDER.
               PERFORM LOG-TRANSACTION.

The period after PROCESS-LARGE-ORDER terminates the IF statement. This means LOG-TRANSACTION is performed unconditionally -- a subtle and dangerous bug. The programmer likely intended both PERFORM statements to be conditional.

The END-IF scope terminator, introduced in COBOL-85, eliminates this entire class of bugs:

      * MODERN COBOL STYLE (ALWAYS USE THIS)
           IF WS-AMOUNT > 1000
               PERFORM PROCESS-LARGE-ORDER
               PERFORM LOG-TRANSACTION
           END-IF

Best practice: Always use END-IF. Never rely on periods to terminate IF statements. The only period in the PROCEDURE DIVISION should be the one at the end of each paragraph or section name and at the end of the last statement in each paragraph.

7.1.3 Nested IF Statements

IF statements can be nested inside other IF statements to handle multi-level decision logic:

       IF WS-CUSTOMER-TYPE = "P"
           IF WS-ORDER-AMOUNT > 5000
               MOVE 15.00 TO WS-DISCOUNT-PCT
           ELSE
               IF WS-ORDER-AMOUNT > 1000
                   MOVE 10.00 TO WS-DISCOUNT-PCT
               ELSE
                   MOVE 5.00 TO WS-DISCOUNT-PCT
               END-IF
           END-IF
       ELSE
           IF WS-ORDER-AMOUNT > 5000
               MOVE 8.00 TO WS-DISCOUNT-PCT
           ELSE
               MOVE 0.00 TO WS-DISCOUNT-PCT
           END-IF
       END-IF

While nesting is sometimes necessary, deeply nested IF statements become difficult to read and maintain. A good rule of thumb is to limit nesting to three levels. When you find yourself going deeper, consider refactoring to use the EVALUATE statement (covered in Section 7.3) or breaking the logic into separate paragraphs.

Readability guidelines for nested IFs:

  • Indent each level consistently (4 spaces is standard)
  • Ensure each IF has a matching END-IF at the same indentation level
  • Add comments at the END-IF to indicate which IF it closes when nesting is deep
  • Consider whether EVALUATE would express the logic more clearly

7.1.4 Compound Conditions: AND, OR, NOT

COBOL supports compound conditions using the logical operators AND, OR, and NOT. These allow you to combine multiple simple conditions into a single complex condition.

AND (Conjunction)

AND requires all conditions to be true for the compound condition to be true:

       IF WS-EMPLOYEE-AGE > 30
           AND WS-YEARS-SERVICE >= 10
           AND WS-PERFORMANCE-RATING = "A"
           DISPLAY "Employee qualifies for promotion."
       END-IF

All three conditions must be true. If any one is false, the entire compound condition is false.

OR (Disjunction)

OR requires at least one condition to be true:

       IF WS-DEPARTMENT = "FINANCE"
           OR WS-DEPARTMENT = "ACCOUNTING"
           OR WS-DEPARTMENT = "AUDIT"
           DISPLAY "Employee is in a financial department."
       END-IF

If any one of the three comparisons is true, the compound condition is true.

NOT (Negation)

NOT reverses the truth value of a condition:

       IF NOT WS-ACCOUNT-STATUS = "CLOSED"
           PERFORM PROCESS-TRANSACTION
       END-IF

NOT can also be applied to compound conditions using parentheses:

       IF NOT (WS-TEMPERATURE < 32 OR WS-WIND-SPEED > 50)
           DISPLAY "Weather conditions are acceptable."
       END-IF

Operator Precedence

When AND and OR appear in the same condition without parentheses, AND has higher precedence than OR. This means:

A OR B AND C

is evaluated as:

A OR (B AND C)

This is the same precedence rule used in most programming languages and in Boolean algebra. However, always use parentheses when mixing AND and OR to make your intent explicit:

      * UNCLEAR - relies on precedence
       IF WS-TYPE = "A" OR WS-TYPE = "B" AND WS-AMT > 1000
           PERFORM PROCESS-SPECIAL
       END-IF

      * CLEAR - parentheses make intent obvious
       IF (WS-TYPE = "A" OR WS-TYPE = "B")
           AND WS-AMT > 1000
           PERFORM PROCESS-SPECIAL
       END-IF

7.1.5 Implied Subjects and Implied Operators

COBOL supports two shorthand notations that can make compound conditions more concise, but can also introduce confusion if not understood properly.

Implied Subject

When comparing the same variable against multiple values with OR, you can omit the repeated subject:

      * Full form
       IF WS-STATE = "NY"
           OR WS-STATE = "NJ"
           OR WS-STATE = "CT"

      * Implied subject form (equivalent)
       IF WS-STATE = "NY" OR "NJ" OR "CT"

Both forms are identical in meaning. The implied subject is the last explicitly named subject -- in this case, WS-STATE.

Implied Operator

Similarly, the relational operator can be implied:

      * Full form
       IF WS-TEMP > WS-HUMIDITY
           AND WS-TEMP > WS-WIND-SPEED

      * Implied operator form (equivalent)
       IF WS-TEMP > WS-HUMIDITY AND WS-WIND-SPEED

The implied operator is the last explicitly stated operator (here, >), and the implied subject is the last explicitly stated subject (here, WS-TEMP).

A Common Trap

Consider this condition:

       IF WS-CODE = "A" OR "B"

A programmer might read this as "if WS-CODE equals A, or if B is true." But that is not what it means. Because of the implied subject rule, it means:

       IF WS-CODE = "A" OR WS-CODE = "B"

Best practice: While implied subjects and operators are valid COBOL, many shops discourage their use because they introduce ambiguity. Write conditions explicitly for maximum clarity, especially in code that will be maintained by teams.


7.2 Relation Conditions

Relation conditions compare two operands using relational operators. COBOL provides both symbolic and word-form operators, and they are completely interchangeable.

7.2.1 Symbol Form

Symbol Meaning
= Equal to
> Greater than
< Less than
>= Greater than or equal to
<= Less than or equal to
NOT = Not equal to

7.2.2 Word Form

Word Form Equivalent Symbol
IS EQUAL TO =
IS GREATER THAN >
IS LESS THAN <
IS GREATER THAN OR EQUAL TO >=
IS LESS THAN OR EQUAL TO <=
IS NOT EQUAL TO NOT =
IS NOT GREATER THAN <=
IS NOT LESS THAN >=

The word IS and the word THAN are optional noise words. All of the following are equivalent:

       IF WS-AMOUNT > 1000
       IF WS-AMOUNT IS GREATER THAN 1000
       IF WS-AMOUNT GREATER THAN 1000
       IF WS-AMOUNT GREATER 1000

Use whichever form reads most naturally in context. Many experienced COBOL programmers prefer the symbolic form for numeric comparisons and the word form when it improves readability.

7.2.3 Comparing Different Data Types

When COBOL compares two operands, the rules depend on the types involved:

  • Numeric to numeric: Algebraic comparison. PIC S9(3) VALUE -5 is less than PIC 9(3) VALUE 3.
  • Alphanumeric to alphanumeric: Character-by-character comparison using the collating sequence (typically ASCII or EBCDIC). Shorter fields are padded with spaces on the right.
  • Numeric to alphanumeric: The numeric operand is converted to its display form, then compared as alphanumeric. This can produce unexpected results -- avoid mixing types in comparisons.

7.3 Sign Conditions

Sign conditions test whether a numeric value is positive, negative, or zero:

       IF WS-BALANCE IS POSITIVE
           DISPLAY "Account has funds."
       END-IF

       IF WS-PROFIT-LOSS IS NEGATIVE
           DISPLAY "Operating at a loss."
       END-IF

       IF WS-NET-CHANGE IS ZERO
           DISPLAY "No change this period."
       END-IF

The relationships are straightforward:

Sign Condition Equivalent Relation
IS POSITIVE > 0
IS NEGATIVE < 0
IS ZERO = 0
IS NOT POSITIVE <= 0
IS NOT NEGATIVE >= 0
IS NOT ZERO NOT = 0

Sign conditions can only be used with numeric operands. The word IS is optional.


7.4 Class Conditions

Class conditions test the nature of the data stored in a field, rather than its value. They are essential for input validation.

7.4.1 Built-in Class Conditions

Class Test True When
NUMERIC All characters are digits (0-9). For signed numeric fields, also allows the sign.
ALPHABETIC All characters are letters (A-Z, a-z) or spaces.
ALPHABETIC-LOWER All characters are lowercase letters (a-z) or spaces.
ALPHABETIC-UPPER All characters are uppercase letters (A-Z) or spaces.
       IF WS-INPUT-FIELD IS NUMERIC
           COMPUTE WS-RESULT = FUNCTION NUMVAL(WS-INPUT-FIELD)
       ELSE
           DISPLAY "Error: non-numeric input"
       END-IF

Important notes about class conditions:

  • The NUMERIC test on a PIC X field checks that every character is a digit (0-9). Spaces, decimal points, signs, and commas are NOT numeric in a PIC X field.
  • Spaces pass the ALPHABETIC, ALPHABETIC-LOWER, and ALPHABETIC-UPPER tests.
  • Class conditions apply to the entire field. You cannot test a substring directly (use reference modification to extract a portion first).

7.4.2 User-Defined Class Conditions

You can define custom class tests in the SPECIAL-NAMES paragraph of the ENVIRONMENT DIVISION:

       ENVIRONMENT DIVISION.
       CONFIGURATION SECTION.
       SPECIAL-NAMES.
           CLASS VALID-PHONE IS "0" THRU "9" "-" "(" ")" " ".
           CLASS HEX-CHAR    IS "0" THRU "9" "A" THRU "F"
                                "a" THRU "f".

These custom classes can then be used just like the built-in ones:

       IF WS-PHONE-NUMBER IS VALID-PHONE
           DISPLAY "Phone format is acceptable."
       ELSE
           DISPLAY "Invalid characters in phone number."
       END-IF

User-defined classes are powerful tools for input validation. They allow you to define exactly which characters are acceptable for a given field, and the resulting condition reads like a business requirement.


7.5 Condition Names (88-Level Items)

Condition names are one of COBOL's most distinctive and powerful features. They attach meaningful names to specific values or ranges of values that a data item can hold, transforming cryptic codes into self-documenting conditions.

7.5.1 Defining Condition Names

Condition names are defined at level 88, immediately following the data item they describe:

       01  WS-ACCOUNT-STATUS      PIC X(1).
           88  ACCOUNT-ACTIVE                 VALUE "A".
           88  ACCOUNT-SUSPENDED              VALUE "S".
           88  ACCOUNT-CLOSED                 VALUE "C".
           88  ACCOUNT-PENDING                VALUE "P".

The 88-level item does not occupy any storage. It is simply a named alias for a particular value of its parent data item.

7.5.2 Using Condition Names in IF Statements

Once defined, a condition name can be used anywhere a condition is expected:

      * With condition name (clear and self-documenting)
       IF ACCOUNT-ACTIVE
           PERFORM PROCESS-TRANSACTION
       END-IF

      * Without condition name (cryptic)
       IF WS-ACCOUNT-STATUS = "A"
           PERFORM PROCESS-TRANSACTION
       END-IF

Both forms are functionally identical, but the condition name version reads like English and does not require the reader to know that "A" means "active."

7.5.3 Multiple Values

A single condition name can represent multiple values:

       01  WS-TRANSACTION-CODE    PIC X(2).
           88  TRANS-CREDIT                   VALUE "DP" "TR".
           88  TRANS-DEBIT                    VALUE "WD" "PY".
           88  VALID-TRANSACTION              VALUE "DP" "WD"
                                                    "TR" "PY".

When you test VALID-TRANSACTION, it is true if WS-TRANSACTION-CODE contains any of the four listed values. This is equivalent to writing:

       IF WS-TRANSACTION-CODE = "DP" OR "WD" OR "TR" OR "PY"

7.5.4 Ranges with THRU

Condition names can specify ranges of values using the THRU keyword:

       01  WS-CREDIT-SCORE        PIC 9(3).
           88  CREDIT-EXCELLENT               VALUE 750 THRU 850.
           88  CREDIT-GOOD                    VALUE 700 THRU 749.
           88  CREDIT-FAIR                    VALUE 650 THRU 699.
           88  CREDIT-POOR                    VALUE 550 THRU 649.
           88  CREDIT-VERY-POOR              VALUE 300 THRU 549.

The range is inclusive on both ends. CREDIT-EXCELLENT is true for any credit score from 750 through 850.

Ranges and discrete values can be combined:

       01  WS-ERROR-CODE          PIC 9(4).
           88  RECOVERABLE-ERROR              VALUE 1000 THRU 3999.
           88  FATAL-ERROR                    VALUE 4000 THRU 5999.
           88  SPECIAL-ERRORS                 VALUE 100 200 300
                                                    9000 THRU 9999.

7.5.5 The SET Statement

The SET statement provides a clean way to assign values to condition names:

       SET ACCOUNT-ACTIVE TO TRUE

This is equivalent to:

       MOVE "A" TO WS-ACCOUNT-STATUS

The SET statement moves the first VALUE associated with the condition name into the parent variable. This means the programmer does not need to know or remember the underlying code -- they simply express their intent.

SET TO FALSE is also supported (when a FALSE value is defined):

       01  WS-OVERDUE-FLAG        PIC X(1).
           88  IS-OVERDUE                     VALUE "Y"
                                              FALSE IS "N".
           88  IS-NOT-OVERDUE                 VALUE "N"
                                              FALSE IS "Y".
       SET IS-OVERDUE TO TRUE       *> WS-OVERDUE-FLAG = "Y"
       SET IS-OVERDUE TO FALSE      *> WS-OVERDUE-FLAG = "N"

Note: SET TO FALSE requires the FALSE IS clause in the 88-level definition. Not all compilers support this extension to the COBOL standard. An alternative pattern is to define complementary condition names, as shown above.

7.5.6 Condition Names as Self-Documenting Code

The real power of condition names is not in saving keystrokes but in creating code that documents itself. Compare these two fragments:

      * WITHOUT condition names (what does "S" mean?)
       IF WS-EMP-STATUS = "S" AND WS-PAY-GRADE >= 12
           IF WS-DEPT-CODE = "FIN" OR "ACC"
               COMPUTE WS-BONUS = WS-SALARY * 0.15
           END-IF
       END-IF

      * WITH condition names (instantly understandable)
       IF EMPLOYEE-SALARIED AND PAY-GRADE-SENIOR
           IF DEPT-FINANCIAL
               COMPUTE WS-BONUS = WS-SALARY * 0.15
           END-IF
       END-IF

When maintaining a system years after it was written, condition names are invaluable. They capture the business meaning of data values at the point of definition, and that meaning flows automatically into every condition that uses them.


7.6 The EVALUATE Statement

The EVALUATE statement is COBOL's multi-way branching construct. Introduced in COBOL-85, it replaced the deeply nested IF chains that plagued earlier COBOL programs. EVALUATE is far more powerful than the SWITCH/CASE statements found in C, Java, or Python -- it supports range matching, multiple subjects, and arbitrary condition testing.

7.6.1 EVALUATE Identifier (Like SWITCH/CASE)

The simplest form of EVALUATE tests a single variable against specific values:

       EVALUATE WS-DAY-OF-WEEK
           WHEN 1
               MOVE "MONDAY"    TO WS-DAY-NAME
           WHEN 2
               MOVE "TUESDAY"   TO WS-DAY-NAME
           WHEN 3
               MOVE "WEDNESDAY" TO WS-DAY-NAME
           WHEN 4
               MOVE "THURSDAY"  TO WS-DAY-NAME
           WHEN 5
               MOVE "FRIDAY"    TO WS-DAY-NAME
           WHEN 6
               MOVE "SATURDAY"  TO WS-DAY-NAME
           WHEN 7
               MOVE "SUNDAY"    TO WS-DAY-NAME
           WHEN OTHER
               MOVE "INVALID"   TO WS-DAY-NAME
       END-EVALUATE

Key points:

  • WHEN OTHER is the default case, executed when no other WHEN matches. It is optional but strongly recommended.
  • There is no fall-through between WHEN clauses. Unlike C's switch statement, only the matching WHEN's statements are executed, and control passes to the statement after END-EVALUATE. No BREAK statement is needed.
  • The first matching WHEN is executed. If multiple WHEN clauses could match, only the first one found is used.

7.6.2 Multiple Values in a Single WHEN

You can stack multiple WHEN clauses to share the same block of statements:

       EVALUATE WS-MONTH
           WHEN 12
           WHEN 1
           WHEN 2
               MOVE "WINTER" TO WS-SEASON
           WHEN 3
           WHEN 4
           WHEN 5
               MOVE "SPRING" TO WS-SEASON
           WHEN 6
           WHEN 7
           WHEN 8
               MOVE "SUMMER" TO WS-SEASON
           WHEN 9
           WHEN 10
           WHEN 11
               MOVE "AUTUMN" TO WS-SEASON
       END-EVALUATE

7.6.3 EVALUATE TRUE (Replacing Nested IF)

EVALUATE TRUE is the most commonly used form of EVALUATE in business programming. Instead of testing a variable against values, it tests arbitrary conditions:

       EVALUATE TRUE
           WHEN WS-SCORE >= 90
               MOVE "A" TO WS-GRADE
           WHEN WS-SCORE >= 80
               MOVE "B" TO WS-GRADE
           WHEN WS-SCORE >= 70
               MOVE "C" TO WS-GRADE
           WHEN WS-SCORE >= 60
               MOVE "D" TO WS-GRADE
           WHEN OTHER
               MOVE "F" TO WS-GRADE
       END-EVALUATE

The keyword TRUE means "match the first WHEN clause whose condition evaluates to true." This is equivalent to an IF/ELSE IF chain, but much cleaner:

      * EQUIVALENT but harder to read:
       IF WS-SCORE >= 90
           MOVE "A" TO WS-GRADE
       ELSE IF WS-SCORE >= 80
           MOVE "B" TO WS-GRADE
       ELSE IF WS-SCORE >= 70
           MOVE "C" TO WS-GRADE
       ...
       END-IF END-IF END-IF END-IF

EVALUATE TRUE is especially powerful with condition names:

       EVALUATE TRUE
           WHEN ACCOUNT-ACTIVE
               PERFORM PROCESS-ACTIVE-ACCOUNT
           WHEN ACCOUNT-SUSPENDED
               PERFORM HANDLE-SUSPENDED
           WHEN ACCOUNT-CLOSED
               PERFORM REJECT-TRANSACTION
           WHEN OTHER
               PERFORM HANDLE-UNKNOWN-STATUS
       END-EVALUATE

7.6.4 EVALUATE with THRU (Range Matching)

The THRU keyword in EVALUATE allows matching ranges of values:

       EVALUATE WS-ANNUAL-INCOME
           WHEN 0 THRU 11000
               MOVE 10.00 TO WS-TAX-RATE
           WHEN 11001 THRU 44725
               MOVE 12.00 TO WS-TAX-RATE
           WHEN 44726 THRU 95375
               MOVE 22.00 TO WS-TAX-RATE
           WHEN OTHER
               MOVE 24.00 TO WS-TAX-RATE
       END-EVALUATE

THRU ranges are inclusive on both ends, just as with 88-level condition names.

7.6.5 EVALUATE with ALSO (Multi-Dimensional Conditions)

The ALSO keyword allows EVALUATE to test multiple subjects simultaneously, creating a decision matrix:

       EVALUATE WS-MEMBER-LEVEL ALSO TRUE
           WHEN "G" ALSO WS-AMOUNT > 5000
               COMPUTE WS-POINTS = WS-AMOUNT * 5
           WHEN "G" ALSO WS-AMOUNT > 1000
               COMPUTE WS-POINTS = WS-AMOUNT * 3
           WHEN "G" ALSO OTHER
               COMPUTE WS-POINTS = WS-AMOUNT * 2
           WHEN "S" ALSO WS-AMOUNT > 1000
               COMPUTE WS-POINTS = WS-AMOUNT * 2
           WHEN "S" ALSO OTHER
               COMPUTE WS-POINTS = WS-AMOUNT * 1
           WHEN OTHER
               MOVE ZEROS TO WS-POINTS
       END-EVALUATE

Each WHEN clause must have the same number of ALSO parts as the EVALUATE subject line. The clause matches only when all parts match simultaneously.

7.6.6 EVALUATE TRUE ALSO TRUE

The most powerful form of EVALUATE uses TRUE ALSO TRUE, allowing arbitrary conditions on all axes:

       EVALUATE TRUE ALSO TRUE
           WHEN FULL-TIME
               ALSO WS-YEARS >= 10
               MOVE 25 TO WS-VACATION-DAYS
           WHEN FULL-TIME
               ALSO WS-YEARS >= 5
               MOVE 20 TO WS-VACATION-DAYS
           WHEN PART-TIME
               ALSO WS-YEARS >= 5
               MOVE 10 TO WS-VACATION-DAYS
           WHEN CONTRACTOR
               ALSO ANY
               MOVE 0 TO WS-VACATION-DAYS
           WHEN OTHER
               MOVE 5 TO WS-VACATION-DAYS
       END-EVALUATE

The ANY keyword matches any value for that position. This is useful when one dimension is irrelevant for a particular case.

7.6.7 EVALUATE FALSE

Less commonly used, EVALUATE FALSE matches the first WHEN clause whose condition is false:

       EVALUATE FALSE
           WHEN ACCOUNT-ACTIVE
               DISPLAY "Account is NOT active"
           WHEN WS-BALANCE > ZERO
               DISPLAY "Balance is NOT positive"
       END-EVALUATE

This is occasionally useful but can be confusing. Most programmers prefer EVALUATE TRUE with negated conditions for clarity.


7.7 CONTINUE and NEXT SENTENCE

7.7.1 CONTINUE (No-Operation Placeholder)

The CONTINUE statement is a no-operation placeholder. It does nothing, but it satisfies the syntax requirement that an IF or WHEN clause must contain at least one statement:

       EVALUATE WS-STATUS
           WHEN "A"
               CONTINUE          *> No action needed for active
           WHEN "S"
               PERFORM HANDLE-SUSPENDED
           WHEN "C"
               PERFORM HANDLE-CLOSED
       END-EVALUATE

CONTINUE is also useful when you want to document that the absence of action is intentional, not an oversight:

       IF WS-DEBUG-MODE = "Y"
           PERFORM WRITE-DEBUG-LOG
       ELSE
           CONTINUE              *> Production mode: no logging
       END-IF

7.7.2 NEXT SENTENCE (Deprecated)

NEXT SENTENCE transfers control to the next sentence (the statement following the next period). It is a relic from pre-COBOL-85 and should not be used in modern code:

      * DEPRECATED -- DO NOT USE
       IF WS-AMOUNT = ZERO
           NEXT SENTENCE
       ELSE
           PERFORM PROCESS-AMOUNT.

The problem with NEXT SENTENCE is that it interacts with the period-terminated sentence structure in ways that are confusing and error-prone. Use CONTINUE instead -- it is always safe and its behavior does not depend on where periods appear.


7.8 De Morgan's Laws in COBOL

De Morgan's laws describe how negation distributes over AND and OR:

  • NOT (A AND B) is equivalent to (NOT A) OR (NOT B)
  • NOT (A OR B) is equivalent to (NOT A) AND (NOT B)

These laws are useful when you need to negate a compound condition or when debugging logic errors. In COBOL:

      * Original condition: heat advisory
       IF WS-TEMPERATURE > 100 AND WS-HUMIDITY > 80
           DISPLAY "HEAT ADVISORY"
       END-IF

      * Negated form using De Morgan's:
       IF WS-TEMPERATURE <= 100 OR WS-HUMIDITY <= 80
           DISPLAY "No heat advisory"
       END-IF

Both tests partition all possible inputs into the same two groups. De Morgan's laws guarantee this equivalence.

When you encounter complex negated conditions in existing code, applying De Morgan's laws can make the logic clearer:

      * Hard to understand:
       IF NOT (WS-A > 10 AND WS-B < 5 AND WS-C = "Y")

      * De Morgan's equivalent (often clearer):
       IF WS-A <= 10 OR WS-B >= 5 OR WS-C NOT = "Y"

7.9 Short-Circuit Evaluation: A COBOL Difference

In languages like C, Java, and Python, compound conditions use short-circuit evaluation: if the first operand of an AND is false, the second operand is not evaluated (because the result must be false regardless). Similarly, if the first operand of an OR is true, the second is not evaluated.

COBOL does not guarantee short-circuit evaluation. The COBOL standard does not specify the order in which conditions are evaluated, and most compilers evaluate all conditions regardless of intermediate results.

This has practical implications:

      * DANGEROUS in COBOL (but safe in C/Java):
       IF WS-DIVISOR NOT = ZERO
           AND (WS-DIVIDEND / WS-DIVISOR) > 10
           PERFORM PROCESS-RESULT
       END-IF

In a language with short-circuit evaluation, the division would not occur when WS-DIVISOR is zero. In COBOL, the division may be attempted, causing a runtime error. Write defensive code:

      * SAFE approach:
       IF WS-DIVISOR NOT = ZERO
           COMPUTE WS-QUOTIENT = WS-DIVIDEND / WS-DIVISOR
           IF WS-QUOTIENT > 10
               PERFORM PROCESS-RESULT
           END-IF
       END-IF

7.10 Performance: EVALUATE vs. Nested IF

A common question is whether EVALUATE is faster or slower than equivalent nested IF statements. The answer: they are essentially equivalent in performance. Modern COBOL compilers optimize both constructs to produce similar machine code.

Choose between them based on readability and maintainability:

Scenario Preferred Construct
Two-way decision IF/ELSE
Testing a single variable against many values EVALUATE identifier
Multi-level decision with many branches EVALUATE TRUE
Testing multiple variables simultaneously EVALUATE with ALSO
Complex boolean expression IF with AND/OR
Range-based decisions EVALUATE with THRU

The real performance concern with conditional logic is algorithmic: if you have hundreds of sequential IF tests that could be replaced by a table lookup, the table lookup will be dramatically faster regardless of whether you use IF or EVALUATE.


7.11 Common Patterns

7.11.1 Validation Logic

Input validation is one of the most common uses of conditional logic. A typical pattern validates multiple fields and collects errors:

       MOVE ZEROS TO WS-ERROR-COUNT

       IF WS-CUSTOMER-NAME = SPACES
           ADD 1 TO WS-ERROR-COUNT
           DISPLAY "Error: Customer name is required"
       END-IF

       IF WS-AMOUNT-INPUT IS NOT NUMERIC
           ADD 1 TO WS-ERROR-COUNT
           DISPLAY "Error: Amount must be numeric"
       END-IF

       IF WS-PHONE IS NOT VALID-PHONE
           ADD 1 TO WS-ERROR-COUNT
           DISPLAY "Error: Invalid phone format"
       END-IF

       IF WS-ERROR-COUNT = ZERO
           PERFORM PROCESS-VALID-INPUT
       ELSE
           PERFORM DISPLAY-ERROR-SUMMARY
       END-IF

7.11.2 Business Rule Engines

Complex business rules can be expressed as a series of condition checks with early exit:

       SET CONTINUE-PROCESSING TO TRUE

       IF CONTINUE-PROCESSING AND NOT ACCOUNT-ACTIVE
           MOVE "Account not active" TO WS-DENIAL-REASON
           SET STOP-PROCESSING TO TRUE
       END-IF

       IF CONTINUE-PROCESSING AND OVER-CREDIT-LIMIT
           MOVE "Over credit limit" TO WS-DENIAL-REASON
           SET STOP-PROCESSING TO TRUE
       END-IF

       IF CONTINUE-PROCESSING AND FRAUD-DETECTED
           MOVE "Fraud flag raised" TO WS-DENIAL-REASON
           SET STOP-PROCESSING TO TRUE
       END-IF

       IF CONTINUE-PROCESSING
           PERFORM APPROVE-TRANSACTION
       ELSE
           PERFORM DENY-TRANSACTION
       END-IF

This pattern is clean, sequential, and easy to extend with new rules.

7.11.3 State Machines

EVALUATE is natural for implementing state machines:

       EVALUATE TRUE
           WHEN ORDER-STATE-NEW
               IF EVENT-PAYMENT-RECEIVED
                   SET ORDER-STATE-PAID TO TRUE
                   PERFORM SEND-CONFIRMATION
               END-IF
           WHEN ORDER-STATE-PAID
               IF EVENT-SHIPPED
                   SET ORDER-STATE-SHIPPED TO TRUE
                   PERFORM SEND-TRACKING
               END-IF
           WHEN ORDER-STATE-SHIPPED
               IF EVENT-DELIVERED
                   SET ORDER-STATE-COMPLETE TO TRUE
                   PERFORM CLOSE-ORDER
               END-IF
       END-EVALUATE

7.12 Common Mistakes and How to Avoid Them

7.12.1 The Premature Period

The most notorious COBOL bug is a period that terminates an IF statement prematurely:

      * BUG: period terminates IF, PERFORM always executes
       IF WS-STATUS = "A"
           DISPLAY "Active".
           PERFORM PROCESS-ACTIVE.

Fix: Use END-IF consistently. Never place a period inside an IF statement's body.

7.12.2 Missing END-IF

Without END-IF, the compiler may pair ELSE with the wrong IF:

      * Ambiguous: which IF does ELSE belong to?
       IF WS-A > 10
           IF WS-B > 20
               DISPLAY "Both conditions met"
           ELSE
               DISPLAY "Which condition failed?"

Without END-IF, the ELSE belongs to the innermost IF (the B test). This may not be the programmer's intent.

Fix: Always use END-IF for every IF statement.

7.12.3 Compound Condition Logic Errors

Misunderstanding operator precedence:

      * BUG: AND binds tighter than OR
      * This means: A=1 OR (B=2 AND C=3), not (A=1 OR B=2) AND C=3
       IF WS-A = 1 OR WS-B = 2 AND WS-C = 3

Fix: Always use parentheses when combining AND and OR.

7.12.4 Comparing Incompatible Types

      * Potential issue: comparing numeric to alphanumeric
       01  WS-AMOUNT     PIC 9(5) VALUE 100.
       01  WS-LIMIT-STR  PIC X(5) VALUE "00100".

       IF WS-AMOUNT = WS-LIMIT-STR     *> Results may surprise

Fix: Compare like types. If you must compare across types, convert explicitly first.

7.12.5 Forgetting That COBOL Does Not Short-Circuit

      * BUG: may divide by zero
       IF WS-COUNT > 0
           AND (WS-TOTAL / WS-COUNT) > THRESHOLD

Fix: Split into nested IFs or compute the result first.


7.13 Fixed-Format vs. Free-Format Examples

All examples in this chapter use fixed-format (traditional COBOL) column layout. For reference, here is the same logic in both formats:

Fixed Format

       IDENTIFICATION DIVISION.
       PROGRAM-ID.  FIXED-FORMAT-DEMO.

       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01  WS-STATUS  PIC X(1).
           88  IS-ACTIVE    VALUE "A".

       PROCEDURE DIVISION.
       MAIN-LOGIC.
           MOVE "A" TO WS-STATUS
           IF IS-ACTIVE
               DISPLAY "Account is active"
           ELSE
               DISPLAY "Account is not active"
           END-IF
           STOP RUN.

Free Format

identification division.
program-id. free-format-demo.

data division.
working-storage section.
01  ws-status  pic x(1).
    88  is-active    value "A".

procedure division.
main-logic.
    move "A" to ws-status
    if is-active
        display "Account is active"
    else
        display "Account is not active"
    end-if
    stop run.

Free-format COBOL (available in COBOL 2002 and later, and supported by GnuCOBOL with the -free flag) removes the column restrictions. The logic and syntax are identical; only the layout rules differ.


7.14 Putting It All Together: A Complete Example

The following example combines IF, EVALUATE, condition names, class conditions, and sign conditions into a single credit card authorization program. See example-06-complex-logic.cob in the code directory for the complete source.

The program implements these business rules:

  1. Card status check -- Is the card active, blocked, stolen, or expired?
  2. Expiration date validation -- Has the card expired based on the current date?
  3. Currency support -- Is the transaction currency supported?
  4. Credit limit check -- Does the cardholder have sufficient available credit?
  5. Transaction velocity -- Has the card been used an unusual number of times today?
  6. Merchant category restrictions -- Is the merchant category allowed for this card?
  7. Fraud risk assessment -- Does the fraud score indicate risk?
  8. Geographic risk -- Is this a cross-border transaction to a high-risk country?

Each check uses condition names to express the business rule clearly. The overall flow uses a "continue processing" flag that allows early termination when a definitive denial is reached, while accumulating warning flags for borderline cases.

This pattern -- sequential rule evaluation with early exit and flag accumulation -- is one of the most common architectures in production COBOL business systems.


Summary

Conditional logic is the engine that drives every business application. In this chapter, you have learned:

  • The IF statement provides two-way branching with optional ELSE and mandatory END-IF scope terminators
  • Compound conditions with AND, OR, and NOT combine simple conditions, with AND taking precedence over OR
  • Implied subjects and operators provide shorthand but can introduce ambiguity -- prefer explicit conditions
  • Relation conditions compare values using both symbolic (=, >, <) and word forms (EQUAL TO, GREATER THAN)
  • Sign conditions (POSITIVE, NEGATIVE, ZERO) test the sign of numeric values
  • Class conditions (NUMERIC, ALPHABETIC, and user-defined) validate the nature of data
  • Condition names (88-levels) attach meaningful names to values, creating self-documenting code
  • SET TRUE provides a clean way to assign values through condition names
  • EVALUATE replaces nested IF chains with clean multi-way branching
  • EVALUATE TRUE tests arbitrary conditions in sequence
  • EVALUATE with THRU matches ranges; EVALUATE with ALSO creates multi-dimensional decision matrices
  • CONTINUE is the preferred no-operation placeholder; NEXT SENTENCE is deprecated
  • COBOL does not guarantee short-circuit evaluation -- write defensive code
  • De Morgan's laws help simplify and debug negated compound conditions

The combination of EVALUATE and 88-level condition names is the signature idiom of well-written COBOL. Master these constructs and you will write code that is not only correct but readable, maintainable, and aligned with the business rules it implements.

In the next chapter, we will explore COBOL's iteration constructs -- the PERFORM statement and its variations -- which, together with the conditional logic you have learned here, give you complete control over program flow.