Program Structure
Hello, World
program hello
implicit none
print *, "Hello, World!"
end program hello >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. HELLO.
PROCEDURE DIVISION.
DISPLAY "Hello, World!"
STOP RUN. A Fortran program is a single
program name ... end program name block. A COBOL program is split into DIVISIONS: IDENTIFICATION DIVISION names the program with PROGRAM-ID, and PROCEDURE DIVISION holds the executable statements. DISPLAY is COBOL's equivalent of Fortran's print *,, and STOP RUN. is the mandatory final statement of the main procedure.Comments
program comments_demo
implicit none
! This is a full-line comment
integer :: total ! This is a trailing comment
total = 10
print *, total
end program comments_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. COMMENTS.
DATA DIVISION.
WORKING-STORAGE SECTION.
*> This is a full-line comment
01 WS-TOTAL PIC 9(3) VALUE 10.
PROCEDURE DIVISION.
DISPLAY WS-TOTAL *> This is a trailing comment
STOP RUN. Fortran comments start with
! and run to the end of the line, exactly like COBOL free-format comments, which start with *>. Both languages inherited comment markers that only exist because of their fixed-column punch-card predecessors, where an asterisk or exclamation point in a specific column marked an entire line as a comment.The Four Divisions
program four_divisions
implicit none
integer :: score
score = 95
print *, "Score:", score
end program four_divisions >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. FOUR-DIVISIONS.
ENVIRONMENT DIVISION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SCORE PIC 9(3) VALUE 95.
PROCEDURE DIVISION.
DISPLAY "Score: " WS-SCORE
STOP RUN. Fortran has no mandatory internal structure beyond declarations before executable statements. COBOL mandates up to four DIVISIONS in a fixed order: IDENTIFICATION (program name), ENVIRONMENT (system/file configuration, often empty for simple programs), DATA (all variable declarations), and PROCEDURE (the executable statements). Every COBOL program follows this exact skeleton regardless of size.
Free-Format Source (Both Languages)
program free_format
implicit none
integer :: value
value = 42
print *, "Value:", value
end program free_format >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. FREE-FORMAT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-VALUE PIC 9(3) VALUE 42.
PROCEDURE DIVISION.
DISPLAY "Value: " WS-VALUE
STOP RUN. Both Fortran and COBOL originally required source code in rigid punch-card columns: Fortran reserved columns 1–5 for labels and column 6 for continuation, while COBOL reserved columns 1–6 for sequence numbers, column 7 for a comment/continuation indicator, and split code into an "Area A" (columns 8–11) and "Area B" (columns 12–72). Modern compilers accept free-format source instead — Fortran since the 1990 standard, COBOL via the
>>SOURCE FORMAT IS FREE directive shown at the top of every COBOL example on this page.IMPLICIT NONE vs. Mandatory PICTURE Clauses
program implicit_none_demo
implicit none
integer :: counter
counter = 1
print *, counter
end program implicit_none_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. PIC-REQUIRED.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-COUNTER PIC 9(3) VALUE 1.
PROCEDURE DIVISION.
DISPLAY WS-COUNTER
STOP RUN. Fortran only enforces explicit variable declarations when
implicit none is present — without it, an undeclared variable silently gets a type from its first letter (a notorious historical bug source). COBOL has no equivalent implicit-typing danger: every data item must be declared in the DATA DIVISION with an explicit PICTURE clause before it can be used, so COBOL enforces what Fortran only enforces by convention.Data Types & Declarations
INTEGER vs. PIC 9
program integer_demo
implicit none
integer :: quantity
quantity = 500
print *, "Quantity:", quantity
end program integer_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. PIC-9-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-QUANTITY PIC 9(5) VALUE 500.
PROCEDURE DIVISION.
DISPLAY "Quantity: " WS-QUANTITY
STOP RUN. Fortran's
integer is a fixed-width binary type (32 bits by default) with a hardware-defined range. COBOL's PIC 9(5) instead declares a decimal field with exactly 5 digits — its range is determined by digit count, not by bits, and it is unsigned unless declared with an S prefix. A COBOL integer field with 5 nines can never silently wrap the way a Fortran integer can overflow past its 32-bit limit; it is a compile-time size constraint, not a binary one.REAL vs. PIC 9V9 (Fixed-Point Decimal)
program real_demo
implicit none
real :: price
price = 19.99
print *, "Price:", price
end program real_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. PIC-V-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-PRICE PIC 9(5)V99 VALUE 19.99.
PROCEDURE DIVISION.
DISPLAY "Price: " WS-PRICE
STOP RUN. Fortran's
real stores 19.99 as an IEEE 754 binary approximation, which can accumulate tiny rounding error across repeated arithmetic. COBOL's V in PIC 9(5)V99 marks an implied decimal point that is never actually stored as a character — the digits on either side are tracked exactly, with no binary approximation at all. This is the single biggest conceptual gap between the two languages for money-handling code: a Fortran programmer must reach for arbitrary-precision libraries to get what COBOL gives for free.CHARACTER(len=n) vs. PIC X(n)
program character_demo
implicit none
character(len=10) :: name
name = "Alice"
print *, "Name: [", trim(name), "]"
end program character_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. PIC-X-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NAME PIC X(10) VALUE "Alice".
PROCEDURE DIVISION.
DISPLAY "Name: [" WS-NAME "]"
STOP RUN. Both
character(len=10) in Fortran and PIC X(10) in COBOL declare a fixed-width, blank-padded text field of exactly 10 bytes — a value shorter than 10 characters is padded with trailing spaces in both languages. Fortran needs trim() to strip the padding before display; COBOL's DISPLAY shows the padding spaces as-is unless the field is explicitly trimmed with a reference modification or intrinsic function.LOGICAL vs. 88-Level Condition Names
program logical_demo
implicit none
logical :: is_active
is_active = .true.
if (is_active) then
print *, "Account is active"
end if
end program logical_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. LEVEL-88-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-STATUS PIC X(8) VALUE "ACTIVE".
88 ACCOUNT-ACTIVE VALUE "ACTIVE".
PROCEDURE DIVISION.
IF ACCOUNT-ACTIVE
DISPLAY "Account is active"
END-IF
STOP RUN. Fortran has a genuine
logical type with literals .true./.false.. COBOL has no boolean type at all — instead, an 88-level condition name attaches a named boolean condition to any field's specific value, so IF ACCOUNT-ACTIVE reads like a boolean test even though WS-STATUS is really an 8-character text field. A Fortran programmer reaching for a COBOL boolean should look for an 88-level, not a data type.KIND System vs. USAGE Clause
program kind_demo
use iso_fortran_env, only: int64
implicit none
integer(kind=int64) :: large_number
large_number = 9000000000_int64
print *, "Large number:", large_number
end program kind_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. COMP-USAGE-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-DISPLAY-NUM PIC 9(8) VALUE 12345678.
01 WS-BINARY-NUM PIC 9(8) COMP VALUE 12345678.
PROCEDURE DIVISION.
DISPLAY WS-DISPLAY-NUM
DISPLAY WS-BINARY-NUM
STOP RUN. Fortran's KIND system (
integer(kind=int64)) controls storage size for performance and range, chosen by the programmer for numerical reasons. COBOL's USAGE clause serves a similar storage-layout role but for a different purpose: the default DISPLAY usage stores one digit per byte as text (for direct compatibility with printed reports and external files), while COMP stores the same value as a packed binary integer for faster arithmetic. Both are storage-format decisions layered on top of a logical value that stays the same either way.PARAMETER vs. VALUE (Constants vs. Initial Values)
program parameter_demo
implicit none
real, parameter :: tax_rate = 0.08
real :: subtotal, total
subtotal = 100.0
total = subtotal * (1.0 + tax_rate)
print *, "Total:", total
end program parameter_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. VALUE-CLAUSE-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TAX-RATE PIC 9V99 VALUE 0.08.
01 WS-SUBTOTAL PIC 9(5)V99 VALUE 100.00.
01 WS-TOTAL PIC 9(5)V99.
PROCEDURE DIVISION.
COMPUTE WS-TOTAL = WS-SUBTOTAL * (1 + WS-TAX-RATE)
DISPLAY "Total: " WS-TOTAL
STOP RUN. Fortran's
parameter attribute creates a true compile-time constant that the compiler rejects any attempt to reassign. COBOL's VALUE clause only sets an initial value — WS-TAX-RATE is an ordinary WORKING-STORAGE field that could be reassigned later with a MOVE, even though this example never does. COBOL has no dedicated named-constant mechanism equivalent to parameter; disciplined naming (and comments) is the only guard against accidental reassignment.Variables & Assignment
Assignment (=) vs. MOVE
program assignment_demo
implicit none
character(len=10) :: greeting
greeting = "Hello"
print *, trim(greeting)
greeting = "Goodbye"
print *, trim(greeting)
end program assignment_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. MOVE-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-GREETING PIC X(10).
PROCEDURE DIVISION.
MOVE "Hello" TO WS-GREETING
DISPLAY WS-GREETING
MOVE "Goodbye" TO WS-GREETING
DISPLAY WS-GREETING
STOP RUN. Fortran uses
= for assignment, reading left-to-right like most languages. COBOL uses the verb MOVE source TO destination, reading right-to-left in terms of data flow. There is no = operator for assignment anywhere in COBOL's PROCEDURE DIVISION — every assignment is spelled out as an English sentence.Assigning One Value to Several Variables
program move_multiple_demo
implicit none
character(len=10) :: source, copy_one, copy_two
source = "Alice"
copy_one = source
copy_two = source
print *, trim(copy_one)
print *, trim(copy_two)
end program move_multiple_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. MOVE-MULTIPLE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SOURCE PIC X(10) VALUE "Alice".
01 WS-COPY-ONE PIC X(10).
01 WS-COPY-TWO PIC X(10).
PROCEDURE DIVISION.
MOVE WS-SOURCE TO WS-COPY-ONE WS-COPY-TWO
DISPLAY WS-COPY-ONE
DISPLAY WS-COPY-TWO
STOP RUN. Fortran needs one assignment statement per destination variable. COBOL's MOVE accepts a list of destinations after TO:
MOVE source TO dest1 dest2 evaluates the source once and copies it into every listed destination in a single statement — there is no Fortran equivalent of this multi-target assignment.Default Initialization
program initial_values_demo
implicit none
integer :: count = 0
character(len=20) :: greeting = "Hello"
print *, count
print *, trim(greeting)
end program initial_values_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. VALUE-INIT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-COUNT PIC 9(5) VALUE 0.
01 WS-GREETING PIC X(20) VALUE "Hello".
PROCEDURE DIVISION.
DISPLAY WS-COUNT
DISPLAY WS-GREETING
STOP RUN. Fortran variables with no initializer hold undefined garbage until assigned, unless declared with an inline initializer like
= 0 (which, as a side effect, gives the variable the SAVE attribute). Every COBOL WORKING-STORAGE field is deterministically initialized when the program starts: to the VALUE clause if present, or to zero (numeric fields) or spaces (alphanumeric fields) if VALUE is absent — COBOL has no concept of an uninitialized field.Derived Types vs. Group Items
program derived_type_demo
implicit none
type :: person
character(len=20) :: first_name
character(len=20) :: last_name
integer :: age
end type person
type(person) :: employee
employee = person("Alice", "Smith", 30)
print *, trim(employee%first_name)
print *, trim(employee%last_name)
print *, employee%age
end program derived_type_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. GROUP-ITEM-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-EMPLOYEE.
05 WS-FIRST-NAME PIC X(20) VALUE "Alice".
05 WS-LAST-NAME PIC X(20) VALUE "Smith".
05 WS-AGE PIC 9(3) VALUE 30.
PROCEDURE DIVISION.
DISPLAY WS-FIRST-NAME
DISPLAY WS-LAST-NAME
DISPLAY WS-AGE
STOP RUN. A Fortran
type declares a reusable named record with fields accessed via %; the type must be declared once and can be instantiated many times. COBOL has no separate named-record declaration — a group item (a level-01 item with subordinate 05-level fields) plays the same structural role, but the layout exists only where it is declared. Field access uses no punctuation at all: WS-FIRST-NAME is referenced directly, with the group/field relationship implied purely by level numbers.Arithmetic
Operators (+, -) vs. ADD / SUBTRACT
program basic_ops_demo
implicit none
integer :: balance
balance = 100
balance = balance + 50
balance = balance - 20
print *, "Balance:", balance
end program basic_ops_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. ADD-SUBTRACT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-BALANCE PIC 9(5) VALUE 100.
PROCEDURE DIVISION.
ADD 50 TO WS-BALANCE
SUBTRACT 20 FROM WS-BALANCE
DISPLAY "Balance: " WS-BALANCE
STOP RUN. Fortran uses the familiar
+ and - infix operators. COBOL's traditional arithmetic verbs are English sentences: ADD n TO field and SUBTRACT n FROM field both modify the destination field in place — there is no separate assignment step, and no operator symbols appear at all. COBOL does also support COMPUTE with ordinary operator syntax, covered separately.Expressions vs. COMPUTE
program compute_demo
implicit none
real :: principal, rate, time_years, interest
principal = 1000.0
rate = 0.05
time_years = 3.0
interest = principal * rate * time_years
print *, "Interest:", interest
end program compute_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. COMPUTE-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-PRINCIPAL PIC 9(6)V99 VALUE 1000.00.
01 WS-RATE PIC 9V999 VALUE 0.050.
01 WS-TIME-YEARS PIC 9(2) VALUE 3.
01 WS-INTEREST PIC 9(6)V99.
PROCEDURE DIVISION.
COMPUTE WS-INTEREST = WS-PRINCIPAL * WS-RATE * WS-TIME-YEARS
DISPLAY "Interest: " WS-INTEREST
STOP RUN. For anything beyond simple ADD/SUBTRACT, COBOL's
COMPUTE statement accepts a full arithmetic expression with + - * / **, resembling Fortran's ordinary assignment expressions far more closely than ADD/SUBTRACT/MULTIPLY/DIVIDE do. Most COBOL style guides recommend COMPUTE for any expression with more than one operator, reserving ADD/SUBTRACT/MULTIPLY/DIVIDE for single-step English-readable operations.MOD Intrinsic vs. DIVIDE ... REMAINDER
program mod_demo
implicit none
integer :: numerator, denominator, quotient, remainder_value
numerator = 17
denominator = 5
quotient = numerator / denominator
remainder_value = mod(numerator, denominator)
print *, "Quotient:", quotient
print *, "Remainder:", remainder_value
end program mod_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. DIVIDE-REMAINDER.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NUMERATOR PIC 9(3) VALUE 17.
01 WS-DENOMINATOR PIC 9(3) VALUE 5.
01 WS-QUOTIENT PIC 9(3).
01 WS-REMAINDER PIC 9(3).
PROCEDURE DIVISION.
DIVIDE WS-NUMERATOR BY WS-DENOMINATOR
GIVING WS-QUOTIENT REMAINDER WS-REMAINDER
DISPLAY "Quotient: " WS-QUOTIENT
DISPLAY "Remainder: " WS-REMAINDER
STOP RUN. Fortran's
mod() intrinsic computes the remainder as a separate call alongside ordinary / integer division. COBOL's DIVIDE ... BY ... GIVING ... REMAINDER ... computes both the quotient and the remainder in a single statement, storing each in its own named field — there is no separate remainder-only verb.⚠ Exact Decimal Arithmetic (Headline Difference)
program precision_demo
implicit none
real :: total
integer :: i
total = 0.0
do i = 1, 10
total = total + 0.1
end do
print *, "Total after adding 0.1 ten times:", total
end program precision_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. EXACT-DECIMAL.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TOTAL PIC 9V9(4) VALUE 0.
01 WS-INDEX PIC 9(2) VALUE 1.
PROCEDURE DIVISION.
PERFORM VARYING WS-INDEX FROM 1 BY 1 UNTIL WS-INDEX > 10
ADD 0.1 TO WS-TOTAL
END-PERFORM
DISPLAY "Total after adding 0.1 ten times: " WS-TOTAL
STOP RUN. Adding
0.1 ten times in Fortran's binary real arithmetic can print something like 1.00000012 instead of exactly 1.0, because 0.1 has no exact binary floating-point representation. COBOL's decimal PIC 9V9(4) field represents 0.1 exactly as the digits "0" and "1000", so the same ten additions produce exactly 1.0000 with zero accumulated error. This is why COBOL, not Fortran or any binary-floating-point language, is the standard for financial ledgers.MIN/MAX Intrinsics vs. Manual IF Comparison
program min_max_demo
implicit none
integer :: score_a, score_b, highest
score_a = 88
score_b = 95
highest = max(score_a, score_b)
print *, "Highest:", highest
end program min_max_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. MAX-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SCORE-A PIC 9(3) VALUE 88.
01 WS-SCORE-B PIC 9(3) VALUE 95.
01 WS-HIGHEST PIC 9(3).
PROCEDURE DIVISION.
IF WS-SCORE-A > WS-SCORE-B
MOVE WS-SCORE-A TO WS-HIGHEST
ELSE
MOVE WS-SCORE-B TO WS-HIGHEST
END-IF
DISPLAY "Highest: " WS-HIGHEST
STOP RUN. Fortran's
max() and min() intrinsics accept two or more arguments and return the extreme value directly. Standard COBOL has no built-in MIN/MAX verb for arbitrary comparisons (GnuCOBOL's FUNCTION MAX covers tables, not two scalar fields in every dialect), so the idiomatic approach is a plain IF ... ELSE comparison followed by a MOVE — three statements where Fortran needs one expression.Strings & Text
String Concatenation (//) vs. STRING
program concat_demo
implicit none
character(len=20) :: first_name, last_name
character(len=41) :: full_name
first_name = "Alice"
last_name = "Smith"
full_name = trim(first_name) // " " // trim(last_name)
print *, trim(full_name)
end program concat_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. STRING-CONCAT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-FIRST-NAME PIC X(20) VALUE "Alice".
01 WS-LAST-NAME PIC X(20) VALUE "Smith".
01 WS-FULL-NAME PIC X(41).
PROCEDURE DIVISION.
STRING FUNCTION TRIM(WS-FIRST-NAME) DELIMITED BY SIZE
" " DELIMITED BY SIZE
FUNCTION TRIM(WS-LAST-NAME) DELIMITED BY SIZE
INTO WS-FULL-NAME
DISPLAY WS-FULL-NAME
STOP RUN. Fortran concatenates strings with the
// operator directly in an expression. COBOL's STRING statement is a full sentence: each source is followed by a DELIMITED BY clause (SIZE means "use the whole field"), and the result is written INTO a destination field. There is no infix concatenation operator in COBOL at all.LEN / LEN_TRIM vs. Fixed PIC X Length
program string_length_demo
implicit none
character(len=20) :: greeting
greeting = "Hello"
print *, "Declared length:", len(greeting)
print *, "Trimmed length:", len_trim(greeting)
end program string_length_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. STRING-LENGTH.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-GREETING PIC X(20) VALUE "Hello".
PROCEDURE DIVISION.
DISPLAY "Declared length: " FUNCTION LENGTH(WS-GREETING)
DISPLAY "Trimmed length: " FUNCTION LENGTH(FUNCTION TRIM(WS-GREETING))
STOP RUN. Fortran's
len() returns the declared width of a character variable, and len_trim() returns the length ignoring trailing blanks. COBOL has no distinct "trimmed length" intrinsic pair — FUNCTION LENGTH always returns the declared PICTURE size unless you first pass the value through FUNCTION TRIM, mirroring Fortran's two-step "declared vs. trimmed" distinction with a slightly different mechanism.INDEX Intrinsic vs. INSPECT TALLYING
program search_demo
implicit none
character(len=30) :: sentence
integer :: position
sentence = "the quick brown fox"
position = index(sentence, "brown")
print *, "Found at position:", position
end program search_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. INSPECT-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SENTENCE PIC X(30) VALUE "the quick brown fox".
01 WS-COUNT PIC 9(2) VALUE 0.
PROCEDURE DIVISION.
INSPECT WS-SENTENCE TALLYING WS-COUNT FOR ALL "brown"
DISPLAY "Occurrences of 'brown': " WS-COUNT
STOP RUN. Fortran's
index() returns the 1-based starting position of a substring, or 0 if not found — directly analogous to a search. COBOL's INSPECT ... TALLYING instead counts how many times a substring occurs, rather than reporting a position. Finding a position in COBOL typically requires reference modification and a PERFORM loop, since INSPECT is built around counting and replacing, not locating.Case Conversion
program case_demo
implicit none
character(len=10) :: name
character(len=10) :: upper_name
name = "alice"
upper_name = name
call to_upper(upper_name)
print *, trim(upper_name)
contains
subroutine to_upper(text)
character(len=*), intent(inout) :: text
integer :: i, code
do i = 1, len(text)
code = iachar(text(i:i))
if (code >= iachar('a') .and. code <= iachar('z')) then
text(i:i) = achar(code - 32)
end if
end do
end subroutine to_upper
end program case_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. CASE-CONVERSION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NAME PIC X(10) VALUE "alice".
PROCEDURE DIVISION.
DISPLAY FUNCTION UPPER-CASE(WS-NAME)
STOP RUN. Fortran has no built-in case-conversion intrinsic in the base language — converting case requires a hand-written loop over character codes with
iachar()/achar(), as shown here. COBOL provides FUNCTION UPPER-CASE and FUNCTION LOWER-CASE directly, a rare case where COBOL's standard library is more convenient than Fortran's for a common text operation.Substring Slicing vs. Reference Modification
program substring_demo
implicit none
character(len=11) :: phrase
phrase = "Hello World"
print *, phrase(1:5)
print *, phrase(7:11)
end program substring_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. REF-MOD-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-PHRASE PIC X(11) VALUE "Hello World".
PROCEDURE DIVISION.
DISPLAY WS-PHRASE(1:5)
DISPLAY WS-PHRASE(7:5)
STOP RUN. Fortran substring syntax
phrase(1:5) gives a start and an end position, both inclusive. COBOL's equivalent, called reference modification, is WS-PHRASE(7:5) — but the second number is a length, not an end position, so it means "5 characters starting at position 7." Porting a Fortran slice to COBOL requires converting the end index into a character count.Arrays & Tables
Array Declaration vs. OCCURS
program array_declaration_demo
implicit none
integer :: scores(3)
scores(1) = 85
scores(2) = 92
scores(3) = 78
print *, scores(1)
print *, scores(2)
print *, scores(3)
end program array_declaration_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. OCCURS-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SCORES-TABLE.
05 WS-SCORE PIC 9(3) OCCURS 3 TIMES.
PROCEDURE DIVISION.
MOVE 85 TO WS-SCORE(1)
MOVE 92 TO WS-SCORE(2)
MOVE 78 TO WS-SCORE(3)
DISPLAY WS-SCORE(1)
DISPLAY WS-SCORE(2)
DISPLAY WS-SCORE(3)
STOP RUN. Fortran declares an array with a dimension in parentheses directly on the type:
integer :: scores(3). COBOL wraps its array (called a table) inside a group item using an OCCURS n TIMES clause on the subordinate field. Both are 1-indexed by default — Fortran and COBOL agree on this, unlike most modern languages.Array Iteration vs. PERFORM VARYING
program iterate_demo
implicit none
integer :: scores(5), i
scores = [85, 92, 78, 95, 88]
do i = 1, 5
print *, scores(i)
end do
end program iterate_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. TABLE-ITERATE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SCORES-TABLE.
05 WS-SCORE PIC 9(3) OCCURS 5 TIMES.
01 WS-INDEX PIC 9(1) VALUE 1.
PROCEDURE DIVISION.
MOVE 85 TO WS-SCORE(1)
MOVE 92 TO WS-SCORE(2)
MOVE 78 TO WS-SCORE(3)
MOVE 95 TO WS-SCORE(4)
MOVE 88 TO WS-SCORE(5)
PERFORM VARYING WS-INDEX FROM 1 BY 1 UNTIL WS-INDEX > 5
DISPLAY WS-SCORE(WS-INDEX)
END-PERFORM
STOP RUN. Fortran iterates a table with an ordinary
do loop over an index variable. COBOL has no do loop at all — the equivalent is PERFORM VARYING index FROM start BY step UNTIL condition, a single statement that combines initialization, the step, and the exit test in one sentence, closer in shape to a C-style for loop than to Fortran's do i = 1, 5.SUM Intrinsic vs. Manual Accumulation
program array_sum_demo
implicit none
integer :: numbers(5), total
numbers = [10, 20, 30, 40, 50]
total = sum(numbers)
print *, "Total:", total
end program array_sum_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. TABLE-SUM.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NUMBERS-TABLE.
05 WS-NUMBER PIC 9(3) OCCURS 5 TIMES.
01 WS-TOTAL PIC 9(6) VALUE 0.
01 WS-INDEX PIC 9(1) VALUE 1.
PROCEDURE DIVISION.
MOVE 10 TO WS-NUMBER(1)
MOVE 20 TO WS-NUMBER(2)
MOVE 30 TO WS-NUMBER(3)
MOVE 40 TO WS-NUMBER(4)
MOVE 50 TO WS-NUMBER(5)
PERFORM VARYING WS-INDEX FROM 1 BY 1 UNTIL WS-INDEX > 5
ADD WS-NUMBER(WS-INDEX) TO WS-TOTAL
END-PERFORM
DISPLAY WS-TOTAL
STOP RUN. Fortran's
sum() intrinsic totals an entire array in one call, operating on the whole array at once. COBOL has no built-in table-sum verb — the standard idiom is a PERFORM VARYING loop that ADDs each element into an accumulator field initialized to zero. What Fortran expresses as a single whole-array operation, COBOL always expresses as an explicit element-by-element loop.Two-Dimensional Arrays vs. Nested OCCURS
program two_d_demo
implicit none
integer :: matrix(3, 3)
matrix = 0
matrix(1, 1) = 1
matrix(2, 2) = 5
matrix(3, 3) = 9
print *, matrix(2, 2)
end program two_d_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. TABLE-2D.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-MATRIX.
05 WS-ROW OCCURS 3 TIMES.
10 WS-CELL PIC 9(3) OCCURS 3 TIMES.
PROCEDURE DIVISION.
MOVE 1 TO WS-CELL(1, 1)
MOVE 5 TO WS-CELL(2, 2)
MOVE 9 TO WS-CELL(3, 3)
DISPLAY WS-CELL(2, 2)
STOP RUN. Fortran declares a 2D array directly with two dimensions:
matrix(3, 3), and both subscripts appear inside one set of parentheses. COBOL builds the same shape by nesting an OCCURS inside another OCCURS at successive level numbers (05 for rows, 10 for columns) — the two-dimensional structure emerges from the nesting rather than from a single combined declaration, though both languages use the same (row, column) subscript order.ALLOCATABLE Arrays vs. OCCURS DEPENDING ON
program allocatable_demo
implicit none
integer, allocatable :: items(:)
integer :: item_count, i
item_count = 3
allocate(items(item_count))
items = [100, 200, 300]
do i = 1, item_count
print *, items(i)
end do
deallocate(items)
end program allocatable_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. OCCURS-DEPENDING.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-ITEM-COUNT PIC 9(3) VALUE 3.
01 WS-ITEMS-TABLE.
05 WS-ITEM PIC 9(5) OCCURS 1 TO 10 TIMES
DEPENDING ON WS-ITEM-COUNT.
01 WS-INDEX PIC 9(3) VALUE 0.
PROCEDURE DIVISION.
MOVE 100 TO WS-ITEM(1)
MOVE 200 TO WS-ITEM(2)
MOVE 300 TO WS-ITEM(3)
PERFORM VARYING WS-INDEX FROM 1 BY 1 UNTIL WS-INDEX > WS-ITEM-COUNT
DISPLAY WS-ITEM(WS-INDEX)
END-PERFORM
STOP RUN. Fortran's
allocatable arrays can be sized at runtime with allocate() and freed with deallocate(), so the maximum size need not be known in advance. COBOL's OCCURS n TO m TIMES DEPENDING ON field instead reserves memory for the maximum size m up front at compile time — nothing is dynamically allocated — and a separate counter field tracks how many of those pre-reserved elements are currently considered valid.Control Flow
IF / ELSE IF vs. IF / ELSE
program if_demo
implicit none
integer :: score
score = 85
if (score >= 90) then
print *, "Grade: A"
else if (score >= 80) then
print *, "Grade: B"
else
print *, "Grade: C or below"
end if
end program if_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. IF-ELSE-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SCORE PIC 9(3) VALUE 85.
PROCEDURE DIVISION.
IF WS-SCORE >= 90
DISPLAY "Grade: A"
ELSE
IF WS-SCORE >= 80
DISPLAY "Grade: B"
ELSE
DISPLAY "Grade: C or below"
END-IF
END-IF
STOP RUN. Fortran offers a dedicated
else if keyword that keeps a multi-branch chain flat. Standard COBOL has no ELSE IF keyword — a chain of conditions is written as nested IF ... ELSE ... END-IF blocks, one level deeper for each additional branch. Every IF needs its own matching END-IF (or a period, in older fixed-format style) to close the scope explicitly.SELECT CASE vs. EVALUATE
program select_case_demo
implicit none
integer :: day_number
day_number = 3
select case (day_number)
case (1)
print *, "Monday"
case (2)
print *, "Tuesday"
case (3)
print *, "Wednesday"
case default
print *, "Some other day"
end select
end program select_case_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. EVALUATE-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-DAY-NUMBER PIC 9(1) VALUE 3.
PROCEDURE DIVISION.
EVALUATE WS-DAY-NUMBER
WHEN 1
DISPLAY "Monday"
WHEN 2
DISPLAY "Tuesday"
WHEN 3
DISPLAY "Wednesday"
WHEN OTHER
DISPLAY "Some other day"
END-EVALUATE
STOP RUN. Fortran's
select case/case/case default maps almost one-to-one onto COBOL's EVALUATE/WHEN/WHEN OTHER — both are structured multi-way branches on a single subject value. COBOL's EVALUATE goes further: it can also test multiple independent conditions at once (EVALUATE TRUE with boolean WHEN clauses), a form with no Fortran equivalent.EVALUATE TRUE vs. a Chain of Conditions
program evaluate_true_demo
implicit none
integer :: temperature
temperature = 75
if (temperature < 32) then
print *, "Freezing"
else if (temperature < 60) then
print *, "Cold"
else if (temperature < 80) then
print *, "Mild"
else
print *, "Hot"
end if
end program evaluate_true_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. EVALUATE-TRUE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TEMPERATURE PIC 9(3) VALUE 75.
PROCEDURE DIVISION.
EVALUATE TRUE
WHEN WS-TEMPERATURE < 32
DISPLAY "Freezing"
WHEN WS-TEMPERATURE < 60
DISPLAY "Cold"
WHEN WS-TEMPERATURE < 80
DISPLAY "Mild"
WHEN OTHER
DISPLAY "Hot"
END-EVALUATE
STOP RUN. EVALUATE TRUE is COBOL's idiom for testing a series of unrelated boolean conditions in order, stopping at the first WHEN clause that is true — functionally equivalent to Fortran's if / else if chain, but expressed through the same EVALUATE machinery used for simple value matching. Many COBOL style guides prefer EVALUATE TRUE over nested IFs for exactly this kind of multi-condition chain..AND./.OR. vs. 88-Level Condition Combination
program logical_ops_demo
implicit none
logical :: is_weekday, is_holiday
is_weekday = .true.
is_holiday = .false.
if (is_weekday .and. .not. is_holiday) then
print *, "Office is open"
end if
end program logical_ops_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. LOGICAL-OPS.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-DAY-TYPE PIC X(8) VALUE "WEEKDAY".
88 IS-WEEKDAY VALUE "WEEKDAY".
01 WS-HOLIDAY-FLAG PIC X(1) VALUE "N".
88 IS-HOLIDAY VALUE "Y".
PROCEDURE DIVISION.
IF IS-WEEKDAY AND NOT IS-HOLIDAY
DISPLAY "Office is open"
END-IF
STOP RUN. Fortran spells its logical operators with surrounding dots:
.and., .or., .not.. COBOL spells the same operators as bare words — AND, OR, NOT — and they combine naturally with 88-level condition names, so IF IS-WEEKDAY AND NOT IS-HOLIDAY reads as an almost literal English sentence built from named conditions rather than raw boolean variables.Loops
DO Loop vs. PERFORM ... TIMES
program do_loop_demo
implicit none
integer :: i
do i = 1, 5
print *, "Iteration:", i
end do
end program do_loop_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. PERFORM-TIMES.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-COUNT PIC 9(1) VALUE 0.
PROCEDURE DIVISION.
PERFORM 5 TIMES
ADD 1 TO WS-COUNT
DISPLAY "Iteration: " WS-COUNT
END-PERFORM
STOP RUN. Fortran's
do i = 1, 5 counts with an explicit loop variable that is visible and usable inside the loop body. COBOL's PERFORM n TIMES only specifies a repetition count — there is no automatic loop variable at all, so this example must maintain its own counter (WS-COUNT) manually with ADD 1 if the iteration number is needed inside the loop.DO WHILE vs. PERFORM UNTIL
program do_while_demo
implicit none
integer :: balance
balance = 1000
do while (balance > 0)
balance = balance - 300
print *, "Balance:", balance
end do
end program do_while_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. PERFORM-UNTIL.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-BALANCE PIC S9(5) VALUE 1000.
PROCEDURE DIVISION.
PERFORM UNTIL WS-BALANCE <= 0
SUBTRACT 300 FROM WS-BALANCE
DISPLAY "Balance: " WS-BALANCE
END-PERFORM
STOP RUN. Fortran's
do while (condition) loops while the condition holds — exactly the semantics of a C-style while loop. COBOL inverts the sense: PERFORM UNTIL condition loops until the condition becomes true, so a Fortran do while (balance > 0) becomes a COBOL PERFORM UNTIL balance <= 0 — the same loop, but the boundary condition must be negated when porting.DO with Step vs. PERFORM VARYING ... BY
program step_demo
implicit none
integer :: i
do i = 10, 0, -2
print *, i
end do
end program step_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. PERFORM-VARYING-STEP.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-INDEX PIC S9(3) VALUE 10.
PROCEDURE DIVISION.
PERFORM VARYING WS-INDEX FROM 10 BY -2 UNTIL WS-INDEX < 0
DISPLAY WS-INDEX
END-PERFORM
STOP RUN. Fortran's three-part
do i = start, stop, step (here counting down by 2) maps directly onto COBOL's PERFORM VARYING index FROM start BY step UNTIL condition — both specify a start value and a step in one line. The difference is the terminating condition: Fortran's stop value is inclusive and the direction is inferred from the sign of the step, while COBOL always requires an explicit UNTIL condition that the programmer must get right for the loop to terminate at the correct point.EXIT/CYCLE vs. EXIT PERFORM / CONTINUE
program exit_cycle_demo
implicit none
integer :: i
do i = 1, 10
if (mod(i, 2) == 0) cycle
if (i > 7) exit
print *, i
end do
end program exit_cycle_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. EXIT-PERFORM-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-INDEX PIC 9(2) VALUE 0.
PROCEDURE DIVISION.
PERFORM VARYING WS-INDEX FROM 1 BY 1 UNTIL WS-INDEX > 10
IF FUNCTION MOD(WS-INDEX, 2) = 0
CONTINUE
ELSE
IF WS-INDEX > 7
EXIT PERFORM
ELSE
DISPLAY WS-INDEX
END-IF
END-IF
END-PERFORM
STOP RUN. Fortran's
cycle skips to the next iteration and exit breaks out of the loop entirely — both are dedicated one-word statements. COBOL's EXIT PERFORM breaks out of the innermost in-line PERFORM loop, but there is no dedicated "skip to next iteration" statement — CONTINUE is COBOL's no-op placeholder, used here inside an IF branch to fall through to the next loop pass without executing anything, the closest equivalent to Fortran's cycle.Subroutines & Paragraphs
SUBROUTINE vs. Paragraph
program subroutine_demo
implicit none
call greet()
call greet()
contains
subroutine greet()
print *, "Hello!"
end subroutine greet
end program subroutine_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. PARAGRAPH-DEMO.
PROCEDURE DIVISION.
PERFORM GREET
PERFORM GREET
STOP RUN.
GREET.
DISPLAY "Hello!". A Fortran
subroutine is a distinct named unit, declared after contains, with its own scope for local variables. A COBOL paragraph is just a label (GREET.) in the PROCEDURE DIVISION followed by statements running to the next label — there is no separate declaration section, no local scope, and no formal parameter list. A paragraph can only read and write the same global WORKING-STORAGE fields as the rest of the program.⚠ INTENT Parameters vs. No Parameter Passing
program intent_demo
implicit none
real :: radius, area
radius = 5.0
call compute_area(radius, area)
print *, "Area:", area
contains
subroutine compute_area(input_radius, area_out)
real, intent(in) :: input_radius
real, intent(out) :: area_out
real, parameter :: pi = 3.14159265358979
area_out = pi * input_radius * input_radius
end subroutine compute_area
end program intent_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. NO-PARAMETERS.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-RADIUS PIC 9(3)V99 VALUE 5.
01 WS-AREA PIC 9(5)V99.
PROCEDURE DIVISION.
PERFORM COMPUTE-AREA
DISPLAY "Area: " WS-AREA
STOP RUN.
COMPUTE-AREA.
COMPUTE WS-AREA = 3.14159 * WS-RADIUS * WS-RADIUS. This is the biggest structural gap between the two languages' procedural models. A Fortran subroutine declares exactly which arguments it reads (
intent(in)), which it produces (intent(out)), and which it does both to (intent(inout)) — the compiler enforces the contract. A standard COBOL paragraph takes no parameters at all: COMPUTE-AREA simply reads and writes WS-RADIUS and WS-AREA because they happen to be global WORKING-STORAGE fields already in scope. Nothing but a comment or a naming convention documents which fields a given paragraph touches.Calling Several Subroutines vs. PERFORM ... THRU
program perform_thru_demo
implicit none
call validate_input()
call process_data()
call print_summary()
contains
subroutine validate_input()
print *, "Validating input..."
end subroutine validate_input
subroutine process_data()
print *, "Processing data..."
end subroutine process_data
subroutine print_summary()
print *, "Summary printed."
end subroutine print_summary
end program perform_thru_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. PERFORM-THRU.
PROCEDURE DIVISION.
PERFORM VALIDATE-INPUT THRU PRINT-SUMMARY
STOP RUN.
VALIDATE-INPUT.
DISPLAY "Validating input...".
PROCESS-DATA.
DISPLAY "Processing data...".
PRINT-SUMMARY.
DISPLAY "Summary printed.". Calling three Fortran subroutines in sequence requires three separate
call statements. COBOL's PERFORM paragraph-1 THRU paragraph-3 executes every paragraph from the first name through the second name, inclusive, in the order they appear in the source file — a single statement that runs a whole contiguous range of paragraphs. This relies entirely on source-code ordering, which makes THRU ranges fragile if paragraphs are reordered or inserted.Array Arguments vs. Operating on Shared Tables
program array_arg_demo
implicit none
real :: measurements(5), mean_value
measurements = [2.5, 3.8, 1.9, 4.2, 3.1]
call compute_mean(measurements, mean_value)
print *, "Mean:", mean_value
contains
subroutine compute_mean(data, mean_out)
real, intent(in) :: data(:)
real, intent(out) :: mean_out
mean_out = sum(data) / real(size(data))
end subroutine compute_mean
end program array_arg_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. TABLE-PARAGRAPH.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-MEASUREMENTS.
05 WS-VALUE PIC 9V9 OCCURS 5 TIMES.
01 WS-MEAN PIC 9V9(4).
01 WS-TOTAL PIC 9(2)V9.
01 WS-INDEX PIC 9(1) VALUE 1.
PROCEDURE DIVISION.
MOVE 2.5 TO WS-VALUE(1)
MOVE 3.8 TO WS-VALUE(2)
MOVE 1.9 TO WS-VALUE(3)
MOVE 4.2 TO WS-VALUE(4)
MOVE 3.1 TO WS-VALUE(5)
PERFORM COMPUTE-MEAN
DISPLAY "Mean: " WS-MEAN
STOP RUN.
COMPUTE-MEAN.
MOVE 0 TO WS-TOTAL
PERFORM VARYING WS-INDEX FROM 1 BY 1 UNTIL WS-INDEX > 5
ADD WS-VALUE(WS-INDEX) TO WS-TOTAL
END-PERFORM
DIVIDE WS-TOTAL BY 5 GIVING WS-MEAN. A Fortran subroutine receives an array as an explicit argument (
data(:)), so it works on whatever array the caller passes in. A COBOL paragraph cannot receive a table as an argument at all — it can only reach the one table already declared in WORKING-STORAGE (WS-MEASUREMENTS). Reusing COMPUTE-MEAN for a different table would mean either duplicating the paragraph or restructuring the data so both tables share the same fields — there is no generic, reusable "function of a table" in traditional COBOL.RECURSIVE Subroutines vs. IS RECURSIVE Programs
program recursive_demo
implicit none
print *, "5! =", factorial(5)
contains
recursive function factorial(n) result(product_value)
integer, intent(in) :: n
integer :: product_value
if (n <= 1) then
product_value = 1
else
product_value = n * factorial(n - 1)
end if
end function factorial
end program recursive_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. RECURSIVE-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-N PIC 9(2) VALUE 5.
01 WS-RESULT PIC 9(6).
PROCEDURE DIVISION.
CALL "FACTORIAL" USING WS-N WS-RESULT
DISPLAY "5! = " WS-RESULT
STOP RUN.
IDENTIFICATION DIVISION.
PROGRAM-ID. FACTORIAL IS RECURSIVE.
DATA DIVISION.
LINKAGE SECTION.
01 LS-N PIC 9(2).
01 LS-RESULT PIC 9(6).
PROCEDURE DIVISION USING LS-N LS-RESULT.
IF LS-N <= 1
MOVE 1 TO LS-RESULT
ELSE
SUBTRACT 1 FROM LS-N
CALL "FACTORIAL" USING LS-N LS-RESULT
ADD 1 TO LS-N
MULTIPLY LS-N BY LS-RESULT
END-IF.
END PROGRAM FACTORIAL.
END PROGRAM RECURSIVE-DEMO. Fortran functions and subroutines are recursive only when explicitly marked with the
recursive keyword — an important safety default, since a normal Fortran subprogram is not guaranteed to be reentrant. GnuCOBOL requires the analogous PROGRAM-ID. name IS RECURSIVE. clause on the specific subprogram that recurses; without it, a self-call has undefined behavior. This example nests FACTORIAL as a genuine subprogram with its own LINKAGE SECTION rather than a plain paragraph, since ordinary PERFORM of a paragraph cannot recurse in COBOL at all — only a real subprogram CALL can.Functions & Intrinsics
FUNCTION vs. CALL ... USING with a Return Field
program user_function_demo
implicit none
print *, "Fahrenheit:", to_fahrenheit(100.0)
contains
function to_fahrenheit(celsius) result(fahrenheit)
real, intent(in) :: celsius
real :: fahrenheit
fahrenheit = celsius * 9.0 / 5.0 + 32.0
end function to_fahrenheit
end program user_function_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. CALL-USING-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-CELSIUS PIC 9(3)V9 VALUE 100.0.
01 WS-FAHRENHEIT PIC 9(3)V9.
PROCEDURE DIVISION.
CALL "TO-FAHRENHEIT" USING WS-CELSIUS WS-FAHRENHEIT
DISPLAY "Fahrenheit: " WS-FAHRENHEIT
STOP RUN.
IDENTIFICATION DIVISION.
PROGRAM-ID. TO-FAHRENHEIT.
DATA DIVISION.
LINKAGE SECTION.
01 LS-CELSIUS PIC 9(3)V9.
01 LS-FAHRENHEIT PIC 9(3)V9.
PROCEDURE DIVISION USING LS-CELSIUS LS-FAHRENHEIT.
COMPUTE LS-FAHRENHEIT = LS-CELSIUS * 9 / 5 + 32.
END PROGRAM TO-FAHRENHEIT.
END PROGRAM CALL-USING-DEMO. A Fortran
function returns a value directly through its name (or a result() clause) and is used in expressions like any built-in operator. COBOL has no user-defined function with a true return value used inline in an expression — the closest equivalent is a separate subprogram (here nested inside the same source file and closed with END PROGRAM) that declares its parameters in a LINKAGE SECTION and receives them via PROCEDURE DIVISION USING. The caller invokes it with CALL "name" USING ... and then reads the output parameter afterward as a separate step — a subprogram call can never be embedded inside an expression the way a Fortran function call can. Note that CALL can only invoke a genuine subprogram declared this way, never a plain PROCEDURE DIVISION paragraph label.Math Intrinsics: sqrt/abs/mod vs. FUNCTION SQRT/ABS/MOD
program math_intrinsics_demo
implicit none
print *, "sqrt(144):", sqrt(144.0)
print *, "abs(-42):", abs(-42)
print *, "mod(17,5):", mod(17, 5)
end program math_intrinsics_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. FUNCTION-INTRINSICS.
PROCEDURE DIVISION.
DISPLAY "sqrt(144): " FUNCTION SQRT(144)
DISPLAY "abs(-42): " FUNCTION ABS(-42)
DISPLAY "mod(17,5): " FUNCTION MOD(17, 5)
STOP RUN. Both languages provide the common math operations as intrinsic functions rather than library calls needing an import — Fortran's
sqrt(), abs(), mod() and COBOL's FUNCTION SQRT, FUNCTION ABS, FUNCTION MOD are functionally identical. The only syntactic difference is that every COBOL intrinsic must be prefixed with the literal keyword FUNCTION, while Fortran calls its intrinsics exactly like any other function.PURE Functions vs. Convention-Only Purity
program pure_demo
implicit none
print *, "Square of 7:", square(7)
contains
pure function square(n) result(product_value)
integer, intent(in) :: n
integer :: product_value
product_value = n * n
end function square
end program pure_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. NO-PURE-KEYWORD.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-N PIC 9(3) VALUE 7.
01 WS-RESULT PIC 9(6).
PROCEDURE DIVISION.
CALL "SQUARE-VALUE" USING WS-N WS-RESULT
DISPLAY "Square of 7: " WS-RESULT
STOP RUN.
IDENTIFICATION DIVISION.
PROGRAM-ID. SQUARE-VALUE.
DATA DIVISION.
LINKAGE SECTION.
01 LS-N PIC 9(3).
01 LS-RESULT PIC 9(6).
PROCEDURE DIVISION USING LS-N LS-RESULT.
*> By convention only: this subprogram should not modify LS-N.
*> COBOL has no compiler-enforced purity guarantee.
MULTIPLY LS-N BY LS-N GIVING LS-RESULT.
END PROGRAM SQUARE-VALUE.
END PROGRAM NO-PURE-KEYWORD. Fortran's
pure keyword tells the compiler a function has no side effects and does not modify its arguments — the compiler verifies this and rejects violations at compile time, which also permits safe use inside array expressions and parallel constructs. COBOL has no equivalent keyword or compiler check: whether a subprogram avoids side effects is purely a matter of programmer discipline, enforced only by a comment like the one in this example.ELEMENTAL Functions vs. Explicit Table Loops
program elemental_demo
implicit none
real :: prices(3)
prices = [10.0, 20.0, 30.0]
print *, apply_discount(prices)
contains
elemental function apply_discount(price) result(discounted)
real, intent(in) :: price
real :: discounted
discounted = price * 0.9
end function apply_discount
end program elemental_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. TABLE-LOOP-DISCOUNT.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-PRICES-TABLE.
05 WS-PRICE PIC 9(3)V99 OCCURS 3 TIMES.
01 WS-INDEX PIC 9(1) VALUE 1.
PROCEDURE DIVISION.
MOVE 10.00 TO WS-PRICE(1)
MOVE 20.00 TO WS-PRICE(2)
MOVE 30.00 TO WS-PRICE(3)
PERFORM VARYING WS-INDEX FROM 1 BY 1 UNTIL WS-INDEX > 3
COMPUTE WS-PRICE(WS-INDEX) = WS-PRICE(WS-INDEX) * 0.9
DISPLAY WS-PRICE(WS-INDEX)
END-PERFORM
STOP RUN. A Fortran
elemental function is written as if it takes one scalar, but the compiler automatically applies it to every element when called with an array argument — apply_discount(prices) discounts all three prices with no explicit loop. COBOL has no analogous automatic broadcasting; applying an operation to every element of a table always requires an explicit PERFORM VARYING loop written out by hand.Output & Formatting
PRINT * vs. DISPLAY
program print_demo
implicit none
integer :: count
real :: price
count = 42
price = 9.99
print *, "Count:", count
print *, "Price:", price
end program print_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. DISPLAY-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-COUNT PIC 9(3) VALUE 42.
01 WS-PRICE PIC 9(2)V99 VALUE 9.99.
PROCEDURE DIVISION.
DISPLAY "Count: " WS-COUNT
DISPLAY "Price: " WS-PRICE
STOP RUN. print *, ... in Fortran and DISPLAY ... in COBOL both write to standard output with default formatting, and both accept a comma- or space-separated list of items to print on one line. The main visual difference is that COBOL numeric fields display with their declared digit width (leading zeros for DISPLAY-usage fields are avoided in practice by choosing an appropriately sized PICTURE), while Fortran prints numbers using compiler-default spacing.FORMAT Descriptors vs. Numeric-Editing PICTUREs
program format_demo
implicit none
real :: amount
amount = 1234.5
write(*, '(a, f10.2)') "Amount: ", amount
end program format_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. NUMERIC-EDITING.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-AMOUNT PIC 9(6)V99 VALUE 1234.50.
01 WS-AMOUNT-DISPLAY PIC $$$,$$$.99.
PROCEDURE DIVISION.
MOVE WS-AMOUNT TO WS-AMOUNT-DISPLAY
DISPLAY "Amount: " WS-AMOUNT-DISPLAY
STOP RUN. Fortran controls output layout with a FORMAT descriptor string passed to
write, such as f10.2 for a fixed-width, 2-decimal field. COBOL takes a completely different approach: a second field is declared with a numeric-editing PICTURE clause ($$$,$$$.99) containing literal punctuation, floating dollar signs, and comma insertion characters. MOVEing the raw numeric value into that edited field automatically inserts the commas, decimal point, and leading dollar sign — formatting is a data-movement operation, not a print-time instruction.Printing Several Values on One Line
program multiple_values_demo
implicit none
character(len=15) :: name
integer :: rank
name = "Alice"
rank = 1
print *, trim(name), rank
end program multiple_values_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. MULTIPLE-DISPLAY.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-NAME PIC X(15) VALUE "Alice".
01 WS-RANK PIC 9(2) VALUE 1.
PROCEDURE DIVISION.
DISPLAY WS-NAME " " WS-RANK
STOP RUN. Fortran's
print *, takes a comma-separated list of items and prints them all on one line with compiler-chosen spacing between them. COBOL's DISPLAY concatenates every operand with no automatic spacing at all — note the literal " " space inserted between WS-NAME and WS-RANK above; omitting it would run the two values together with no gap.Internal Files vs. STRING for Building Text
program internal_io_demo
implicit none
character(len=20) :: label_text
integer :: year
year = 2026
write(label_text, '("Report_", i4, ".txt")') year
print *, trim(label_text)
end program internal_io_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. STRING-BUILDING.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-YEAR PIC 9(4) VALUE 2026.
01 WS-LABEL-TEXT PIC X(20).
PROCEDURE DIVISION.
STRING "Report_" DELIMITED BY SIZE
WS-YEAR DELIMITED BY SIZE
".txt" DELIMITED BY SIZE
INTO WS-LABEL-TEXT
DISPLAY WS-LABEL-TEXT
STOP RUN. Fortran's "internal file" feature reuses the ordinary
write statement with a character variable as the unit, giving an sprintf-like way to build formatted text. COBOL has no internal-file mechanism — the equivalent tool for assembling text from pieces is the same STRING statement used for concatenation, which happily interleaves literals and numeric fields (a numeric field is converted to its DISPLAY-usage digits automatically).File I/O
OPEN/WRITE/CLOSE vs. SELECT/FD/OPEN/WRITE
program file_write_demo
implicit none
integer :: file_unit
open(unit=10, file="output.txt", status="replace", action="write")
write(10, *) "Alice,30"
write(10, *) "Bob,25"
close(10)
end program file_write_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. FILE-WRITE.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT OUTPUT-FILE
ASSIGN TO "output.txt"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD OUTPUT-FILE.
01 WS-OUTPUT-RECORD PIC X(80).
PROCEDURE DIVISION.
OPEN OUTPUT OUTPUT-FILE
MOVE "Alice,30" TO WS-OUTPUT-RECORD
WRITE WS-OUTPUT-RECORD
MOVE "Bob,25" TO WS-OUTPUT-RECORD
WRITE WS-OUTPUT-RECORD
CLOSE OUTPUT-FILE
STOP RUN. Fortran opens a file directly with
open(unit=10, file=..., action="write") and writes to it by unit number — no separate record layout is required. COBOL requires the file to be declared across two DIVISIONS before any PROCEDURE DIVISION statement touches it: SELECT/ASSIGN in the ENVIRONMENT DIVISION names and locates the file, and an FD entry in the DATA DIVISION's FILE SECTION declares the record layout (WS-OUTPUT-RECORD) that every WRITE actually writes. Both examples require a real filesystem and cannot run in the browser sandbox.iostat Loop vs. AT END Condition
program file_read_demo
implicit none
integer :: file_unit, io_status
character(len=80) :: line_text
open(unit=11, file="names.txt", status="old", action="read")
do
read(11, '(a)', iostat=io_status) line_text
if (io_status /= 0) exit
print *, trim(line_text)
end do
close(11)
end program file_read_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. FILE-READ.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT INPUT-FILE
ASSIGN TO "names.txt"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD INPUT-FILE.
01 WS-RECORD PIC X(80).
WORKING-STORAGE SECTION.
01 WS-END-OF-FILE PIC X(1) VALUE "N".
88 END-OF-FILE VALUE "Y".
PROCEDURE DIVISION.
OPEN INPUT INPUT-FILE
READ INPUT-FILE
AT END SET END-OF-FILE TO TRUE
END-READ
PERFORM UNTIL END-OF-FILE
DISPLAY WS-RECORD
READ INPUT-FILE
AT END SET END-OF-FILE TO TRUE
END-READ
END-PERFORM
CLOSE INPUT-FILE
STOP RUN. Fortran detects end-of-file through the
iostat argument on read, which returns a non-zero status once there is nothing left to read — checked explicitly after every read inside a bare do/exit loop. COBOL uses the AT END clause directly on the READ statement combined with an 88-level condition, following the "priming read" pattern: one READ before the loop starts, and one at the bottom of every iteration, so the AT END condition is always checked immediately after each read attempt. Both require a real filesystem.Unit Numbers vs. Named File Handles
program unit_number_demo
implicit none
open(unit=20, file="log.txt", status="replace", action="write")
open(unit=21, file="errors.txt", status="replace", action="write")
write(20, *) "Log entry"
write(21, *) "Error entry"
close(20)
close(21)
end program unit_number_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. NAMED-HANDLES.
ENVIRONMENT DIVISION.
INPUT-OUTPUT SECTION.
FILE-CONTROL.
SELECT LOG-FILE
ASSIGN TO "log.txt"
ORGANIZATION IS LINE SEQUENTIAL.
SELECT ERROR-FILE
ASSIGN TO "errors.txt"
ORGANIZATION IS LINE SEQUENTIAL.
DATA DIVISION.
FILE SECTION.
FD LOG-FILE.
01 WS-LOG-RECORD PIC X(80).
FD ERROR-FILE.
01 WS-ERROR-RECORD PIC X(80).
PROCEDURE DIVISION.
OPEN OUTPUT LOG-FILE
OPEN OUTPUT ERROR-FILE
MOVE "Log entry" TO WS-LOG-RECORD
WRITE WS-LOG-RECORD
MOVE "Error entry" TO WS-ERROR-RECORD
WRITE WS-ERROR-RECORD
CLOSE LOG-FILE
CLOSE ERROR-FILE
STOP RUN. Fortran identifies an open file by an arbitrary integer
unit number chosen by the programmer — write(20, *) and write(21, *) distinguish the two files purely by number. COBOL identifies each file by a programmer-chosen name declared once in SELECT (LOG-FILE, ERROR-FILE) and used by that name everywhere afterward — no arbitrary integers to keep track of, but a separate FD and record layout is required for every distinct file. Both require a real filesystem.⚠ Gotchas for Fortran Programmers
⚠ COBOL Keywords and Names Are Case-Insensitive
program case_sensitivity_demo
implicit none
! Fortran is also case-insensitive for identifiers,
! but this trips up programmers coming from C-family languages
integer :: Total, TOTAL_VALUE
Total = 10
TOTAL_VALUE = 20
print *, total, TOTAL_value
end program case_sensitivity_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. CASE-INSENSITIVE.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-TOTAL PIC 9(3) VALUE 10.
PROCEDURE DIVISION.
display ws-total
DISPLAY WS-TOTAL
Display Ws-Total
STOP RUN. This is one place where Fortran and COBOL genuinely agree, unlike most modern languages: both treat identifiers and reserved words as case-insensitive, so
DISPLAY, display, and Display are the same keyword. Convention in both languages is to write reserved words and DIVISION/SECTION headers in uppercase and everything else in a consistent case, purely for human readability — the compiler does not care either way.⚠ MOVE Truncates and Pads Silently
program fixed_width_demo
implicit none
character(len=5) :: short_field
short_field = "Alexander" ! Truncated at compile/assignment time to "Alexa"
print *, "[", short_field, "]"
end program fixed_width_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. MOVE-TRUNCATION.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SHORT-FIELD PIC X(5).
PROCEDURE DIVISION.
MOVE "Alexander" TO WS-SHORT-FIELD
DISPLAY "[" WS-SHORT-FIELD "]"
STOP RUN. Assigning a 9-character string into a 5-character field silently truncates it to "Alexa" in both languages — there is no error, warning, or exception in either Fortran or COBOL. This is a direct consequence of both languages' fixed-width character storage. A Fortran programmer already knows to size
character(len=n) fields generously; the same discipline must be applied to every COBOL PIC X(n) field, since COBOL applies exactly the same silent-truncation rule on MOVE.⚠ Silent Digit Truncation Without ON SIZE ERROR
program numeric_overflow_demo
implicit none
integer(kind=1) :: tiny_value
tiny_value = 127
tiny_value = tiny_value + 1 ! Overflows silently to -128 (two's complement wraparound)
print *, tiny_value
end program numeric_overflow_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. SIZE-ERROR-DEMO.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-SMALL-FIELD PIC 9(2) VALUE 95.
PROCEDURE DIVISION.
ADD 10 TO WS-SMALL-FIELD
DISPLAY WS-SMALL-FIELD
ADD 10 TO WS-SMALL-FIELD ON SIZE ERROR
DISPLAY "Overflow detected!"
END-ADD
STOP RUN. A Fortran integer silently wraps around on overflow (two's complement, no warning). Without the optional
ON SIZE ERROR clause, COBOL arithmetic that overflows a field's declared PICTURE also fails silently, simply truncating the high-order digits — the first ADD 10 TO WS-SMALL-FIELD above (95 + 10 = 105 into a 2-digit field) silently stores 05. Unlike Fortran, COBOL offers an explicit opt-in safety net: adding ON SIZE ERROR to any arithmetic statement lets the program detect and handle the overflow instead of losing digits silently, as the second ADD demonstrates.⚠ The Period Is a Statement Terminator, Not Punctuation
program period_demo
implicit none
integer :: value
value = 5
if (value > 0) then
print *, "Positive"
end if
end program period_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. PERIOD-TERMINATOR.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-VALUE PIC S9(3) VALUE 5.
PROCEDURE DIVISION.
IF WS-VALUE > 0
DISPLAY "Positive"
END-IF.
STOP RUN. Fortran statements are terminated by a newline, with no equivalent of a required trailing punctuation mark. A COBOL period (
.) is not decorative — it terminates a sentence and, in older code, can end an entire paragraph. A misplaced or missing period is one of COBOL's most infamous bug sources: a stray period after an IF can silently truncate the scope of that IF, executing statements unconditionally that were meant to be inside the conditional block.⚠ No User Functions Usable Inside Expressions
program function_in_expression_demo
implicit none
real :: base_value, doubled_and_squared
base_value = 3.0
doubled_and_squared = square(double_value(base_value))
print *, doubled_and_squared
contains
function double_value(x) result(y)
real, intent(in) :: x
real :: y
y = x * 2.0
end function double_value
function square(x) result(y)
real, intent(in) :: x
real :: y
y = x * x
end function square
end program function_in_expression_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. NO-NESTED-CALLS.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-BASE-VALUE PIC 9(3)V9 VALUE 3.
01 WS-DOUBLED PIC 9(3)V9.
01 WS-RESULT PIC 9(5)V9.
PROCEDURE DIVISION.
CALL "DOUBLE-VALUE" USING WS-BASE-VALUE WS-DOUBLED
CALL "SQUARE-VALUE" USING WS-DOUBLED WS-RESULT
DISPLAY WS-RESULT
STOP RUN.
IDENTIFICATION DIVISION.
PROGRAM-ID. DOUBLE-VALUE.
DATA DIVISION.
LINKAGE SECTION.
01 LS-INPUT PIC 9(3)V9.
01 LS-OUTPUT PIC 9(3)V9.
PROCEDURE DIVISION USING LS-INPUT LS-OUTPUT.
COMPUTE LS-OUTPUT = LS-INPUT * 2.
END PROGRAM DOUBLE-VALUE.
IDENTIFICATION DIVISION.
PROGRAM-ID. SQUARE-VALUE.
DATA DIVISION.
LINKAGE SECTION.
01 LS-INPUT PIC 9(3)V9.
01 LS-OUTPUT PIC 9(5)V9.
PROCEDURE DIVISION USING LS-INPUT LS-OUTPUT.
COMPUTE LS-OUTPUT = LS-INPUT * LS-INPUT.
END PROGRAM SQUARE-VALUE.
END PROGRAM NO-NESTED-CALLS. A Fortran programmer freely composes function calls inside an expression, as in
square(double_value(base_value)) — the result of one call becomes the argument to the next, with no intermediate variable required. COBOL's CALL statement cannot appear inside an expression at all: it is a standalone statement that writes its answer into a USING parameter of a separately declared subprogram, so composing two operations always requires an explicit intermediate field (WS-DOUBLED here) and two separate CALL statements, never a nested call.Both Languages Are 1-Indexed (A Rare Point of Agreement)
program one_based_demo
implicit none
integer :: values(5)
values = [10, 20, 30, 40, 50]
print *, "First element:", values(1)
print *, "Last element:", values(5)
end program one_based_demo >>SOURCE FORMAT IS FREE
IDENTIFICATION DIVISION.
PROGRAM-ID. ONE-BASED.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-VALUES-TABLE.
05 WS-VALUE PIC 9(3) OCCURS 5 TIMES.
PROCEDURE DIVISION.
MOVE 10 TO WS-VALUE(1)
MOVE 20 TO WS-VALUE(2)
MOVE 30 TO WS-VALUE(3)
MOVE 40 TO WS-VALUE(4)
MOVE 50 TO WS-VALUE(5)
DISPLAY "First element: " WS-VALUE(1)
DISPLAY "Last element: " WS-VALUE(5)
STOP RUN. Unlike most languages a Fortran programmer might compare against (C, Python, Ruby, and their 0-based descendants), COBOL tables are 1-indexed by default — exactly like Fortran arrays. This is one of the few places moving between these two languages requires no mental index adjustment at all: the first element of both a Fortran array and a COBOL table is subscript 1, and the last element of a 5-item collection is subscript 5 in both.