COBOL Textbook

COMPUTE, ROUNDED, ON SIZE ERROR: COBOL arithmetic without overflow

One GnuCOBOL program: COMPUTE with and without ROUNDED, ROUNDED MODE IS NEAREST-EVEN on a tie, an overflow caught by ON SIZE ERROR, and a trapped divide by zero.

Published
Updated
Dialect
GnuCOBOL 3.x, free format
Status
Compiled and run with GnuCOBOL 3.2
Length
1,931 words, 9 min

COBOL arithmetic is decimal, exact, and silent about overflow unless you ask. This lesson is about the asking. One program computes an invoice total with and without ROUNDED, rounds a tie two different ways with ROUNDED MODE IS, overflows a three-digit field once with ON SIZE ERROR and once without, and divides by zero without crashing. At the end you will know which of those the language protects you from by default (none of them) and what each clause costs to add (one line).

The listing is desk-checked against the GnuCOBOL 3.2 Programmer's Guide, sections 7.8.9, 7.6.6 and 7.6.7, and against IBM's pages on the ROUNDED and SIZE ERROR phrases. The expected values were first worked by hand and checked with a short Python calculation, both shown below; on 29 October 2026 we compiled and ran the program with GnuCOBOL 3.2, and it printed exactly that output. The storage rules it relies on are in the PICTURE lesson, and the truncation behaviour it contrasts with is in the MOVE lesson.

The program

       >>SOURCE FORMAT IS FREE
*> cobol-compute-rounded.cob - COMPUTE with and without
*>   ROUNDED, ROUNDED MODE IS NEAREST-EVEN, an overflow trapped by
*>   ON SIZE ERROR, the same overflow untrapped, and a divide by zero
*> Dialect: GnuCOBOL 3.x, free format
*> Status : desk-checked against the GnuCOBOL 3.2 Programmer's Guide;
*>          compiled and run with GnuCOBOL 3.2 on 2026-10-29
*> Inputs : none
*> Outputs: nine lines on standard output
*> Build  : cobc -x cobol-compute-rounded.cob
*> Run    : ./cobol-compute-rounded
*>          (Windows: cobol-compute-rounded.exe)

IDENTIFICATION DIVISION.
PROGRAM-ID. COMPUTE-DEMO.

ENVIRONMENT DIVISION.

DATA DIVISION.
WORKING-STORAGE SECTION.
01 WS-Price          PIC 9(3)V99  VALUE 19.99.
01 WS-Qty            PIC 9(3)     VALUE 3.
01 WS-Tax-Rate       PIC V999     VALUE 0.075.
01 WS-Total-Trunc    PIC 9(5)V99.
01 WS-Total-Round    PIC 9(5)V99.

01 WS-In-A           PIC 9V999    VALUE 2.345.
01 WS-In-B           PIC 9V999    VALUE 2.355.
01 WS-Out-Default    PIC 9V99.
01 WS-Out-Even       PIC 9V99.

01 WS-Small          PIC 9(3)     VALUE 5.
01 WS-Big            PIC 9(5)     VALUE 12345.
01 WS-Divisor        PIC 9        VALUE 0.
01 WS-Result         PIC 9(5)V99  VALUE 0.

PROCEDURE DIVISION.
MAIN-PARAGRAPH.
*>  1. The same expression, truncated and rounded.
    COMPUTE WS-Total-Trunc = WS-Price * WS-Qty * (1 + WS-Tax-Rate)
    COMPUTE WS-Total-Round ROUNDED
                         = WS-Price * WS-Qty * (1 + WS-Tax-Rate)
    DISPLAY "Total, no ROUNDED   : " WS-Total-Trunc
    DISPLAY "Total, ROUNDED      : " WS-Total-Round

*>  2. Rounding modes on a tie: 2.345 and 2.355 into two decimals.
    COMPUTE WS-Out-Default ROUNDED = WS-In-A
    COMPUTE WS-Out-Even ROUNDED MODE IS NEAREST-EVEN = WS-In-A
    DISPLAY "2.345 default mode  : " WS-Out-Default
    DISPLAY "2.345 NEAREST-EVEN  : " WS-Out-Even
    COMPUTE WS-Out-Default ROUNDED = WS-In-B
    COMPUTE WS-Out-Even ROUNDED MODE IS NEAREST-EVEN = WS-In-B
    DISPLAY "2.355 default mode  : " WS-Out-Default
    DISPLAY "2.355 NEAREST-EVEN  : " WS-Out-Even

*>  3. Overflow trapped: the receiver keeps its old value.
    COMPUTE WS-Small = WS-Big
        ON SIZE ERROR
            DISPLAY "Size error trapped, WS-Small still " WS-Small
        NOT ON SIZE ERROR
            DISPLAY "No size error, WS-Small now " WS-Small
    END-COMPUTE

*>  4. The same overflow untrapped: leading digits vanish silently.
    COMPUTE WS-Small = WS-Big
    DISPLAY "Untrapped overflow, WS-Small now " WS-Small

*>  5. Division by zero, trapped instead of aborting the run.
    COMPUTE WS-Result = WS-Price / WS-Divisor
        ON SIZE ERROR
            DISPLAY "Divide by zero trapped, WS-Result still " WS-Result
    END-COMPUTE
    STOP RUN.
Listing 1.4-1. Lines 1 to 73 of cobol-compute-rounded.cob.

COMPUTE and the intermediate result

COMPUTE evaluates an arithmetic expression, with the usual operators and parentheses plus ** for exponentiation, and stores the result in one or more receiving items. The Programmer's Guide describes it as the alternative to chains of ADD, SUBTRACT, MULTIPLY and DIVIDE, and for anything beyond a single operation it is also clearer. The expression is evaluated in an intermediate result with enough decimal places to be exact for these operands, and only the final store into the receiver truncates or rounds. That last step is where the clauses in this lesson apply.

Step 1 computes 19.99 * 3 * 1.075. The exact value is 64.46775. Stored into PIC 9(5)V99 without ROUNDED, the digits beyond two decimals are simply dropped, giving 64.46. The guide's rule is blunt: absence of a ROUNDED clause "is the same as specifying ROUNDED MODE IS TRUNCATION". With ROUNDED, the result is 64.47. IBM's page on the phrase states the classic rule: the least significant digit of the receiver "is increased by 1 whenever the most significant digit of the excess is greater than or equal to 5".

Eight rounding modes

The 2002 standard added ROUNDED MODE IS, and GnuCOBOL implements all eight modes; the guide lists them with worked examples. IBM's ROUNDED page for Enterprise COBOL 6.4 documents only the classic round-half-away behaviour and says nothing of MODE IS, so treat the modes as portable to standard-conforming compilers and check before relying on them elsewhere. The table condenses the guide's examples for an integer receiver.

Table 1.4-1
Mode Rule (Programmer's Guide 7.6.7) +2.5 -2.5 +3.5
AWAY-FROM-ZERO nearest value of larger magnitude 3 -3 4
NEAREST-AWAY-FROM-ZERO nearest; ties go to larger magnitude (the default for ROUNDED) 3 -3 4
NEAREST-EVEN nearest; ties go to the even digit ("banker's rounding") 2 -2 4
NEAREST-TOWARD-ZERO nearest; ties go to smaller magnitude 2 -2 3
PROHIBITED no rounding allowed; sets the EC-SIZE-TRUNCATION exception, result undefined undefined undefined undefined
TOWARD-GREATER toward the algebraically larger value 3 -2 4
TOWARD-LESSER toward the algebraically smaller value 2 -3 3
TRUNCATION toward zero (the default when ROUNDED is absent) 2 -2 3

Step 2 puts the two modes that matter for money side by side. ROUNDED on its own means NEAREST-AWAY-FROM-ZERO, so 2.345 becomes 2.35 and 2.355 becomes 2.36. NEAREST-EVEN sends a tie to whichever neighbour ends in an even digit: 2.345 becomes 2.34 (4 is even) and 2.355 becomes 2.36 (6 is even). Over thousands of line items the two policies drift apart by a few cents, which is exactly why a ledger specification names its rounding rule and why the mode clause is worth knowing.

ON SIZE ERROR

A size error, in the guide's words, is either "a result too large for the receiving field" or an attempt to divide by zero. IBM's page adds the precise definition: the absolute value of the result, after decimal-point alignment, exceeds the largest value the receiver can hold. Steps 3 and 4 move the five-digit value 12345 into the three-digit WS-Small twice. With ON SIZE ERROR, the guide and IBM agree on the outcome: the receiver is not changed, and the imperative statement after the clause runs. So WS-Small still holds 5, and the NOT ON SIZE ERROR branch is skipped. Without the clause, IBM says "truncation rules apply and the value of the affected resultant identifier is computed", which means the high-order digits are lost and WS-Small becomes 345. The guide's own words for the untrapped case are that overflow conditions "occur silently, usually without any runtime messages".

Step 5 divides by a zero divisor. The guide is specific about the difference from overflow: without ON SIZE ERROR, a division by zero prints a runtime error message on standard error and aborts the program. With the clause, the division is trapped, the receiver is untouched, and the run continues. IBM's rule is the same, stated as "the program is terminated abnormally" when the clause is absent. If a batch job has ever died halfway through a file with a divide-by-zero message, this one clause is the fix.

Compile and run

cobc -x cobol-compute-rounded.cob
./cobol-compute-rounded

Worked example

The output, derived by hand from the rules above and identical to what the compiled program printed (with GnuCOBOL's default pretty-display setting inserting the decimal point in numeric displays):

Total, no ROUNDED   : 00064.46
Total, ROUNDED      : 00064.47
2.345 default mode  : 2.35
2.345 NEAREST-EVEN  : 2.34
2.355 default mode  : 2.36
2.355 NEAREST-EVEN  : 2.36
Size error trapped, WS-Small still 005
Untrapped overflow, WS-Small now 345
Divide by zero trapped, WS-Result still 00000.00

Two lines that could have appeared do not: the NOT ON SIZE ERROR branch never runs, and no runtime message is printed, because the divide is trapped. We checked the arithmetic of steps 1 and 2 in Python with exact decimals, which we did run:

from decimal import Decimal, ROUND_DOWN, ROUND_HALF_UP, ROUND_HALF_EVEN
total = Decimal("19.99") * 3 * (1 + Decimal("0.075"))          # 64.46775
print(total.quantize(Decimal("0.01"), ROUND_DOWN))              # 64.46
print(total.quantize(Decimal("0.01"), ROUND_HALF_UP))           # 64.47
for v in ("2.345", "2.355"):
    d = Decimal(v)
    print(v, d.quantize(Decimal("0.01"), ROUND_HALF_UP),
             d.quantize(Decimal("0.01"), ROUND_HALF_EVEN))       # 2.35 2.34 / 2.36 2.36

Python's ROUND_HALF_UP is the same policy as COBOL's NEAREST-AWAY-FROM-ZERO for positive values, and ROUND_HALF_EVEN matches NEAREST-EVEN.

Eight modes, one tie table, compiled

Step 2 tests two modes on two values. To see all eight at once, a second program, cobol-rounded-mode-ties.cob, stores twelve values into a PIC S9V99 receiver under every ROUNDED MODE, plus plain ROUNDED and no ROUNDED at all. The values are four exact ties (plus and minus 2.345 and 2.355), four near-ties one ten-thousandth either side of a tie (plus and minus 2.3449 and 2.3451), a tie at the smallest scale (plus and minus 0.005), a value that needs no rounding (2.34, held as 2.3400 in a four-decimal item), and a tie that rounds past the receiver's capacity (9.995). Every statement has ON SIZE ERROR, and a cell reads SIZE when that branch ran; the receiver is reset to zero before each statement.

On 30 October 2026 we compiled and ran it with GnuCOBOL 3.2 (cobc (GnuCOBOL) 3.2.0, the MSYS2 MinGW-w64 build on Windows) three times: cobc -x, cobc -x -std=ibm and cobc -x -std=mf. The three runs printed the same bytes, so this one block is the output of all three:

value    none   ROUND  AWAY   N-AWAY N-EVEN N-TOW  PROHIB T-GRT  T-LSR  TRUNC
 2.3450  2.34   2.35   2.35   2.35   2.34   2.34  SIZE    2.35   2.34   2.34
 2.3550  2.35   2.36   2.36   2.36   2.36   2.35  SIZE    2.36   2.35   2.35
-2.3450 -2.34  -2.35  -2.35  -2.35  -2.34  -2.34  SIZE   -2.34  -2.35  -2.34
-2.3550 -2.35  -2.36  -2.36  -2.36  -2.36  -2.35  SIZE   -2.35  -2.36  -2.35
 2.3449  2.34   2.34   2.35   2.34   2.34   2.34  SIZE    2.35   2.34   2.34
 2.3451  2.34   2.35   2.35   2.35   2.35   2.35  SIZE    2.35   2.34   2.34
-2.3449 -2.34  -2.34  -2.35  -2.34  -2.34  -2.34  SIZE   -2.34  -2.35  -2.34
-2.3451 -2.34  -2.35  -2.35  -2.35  -2.35  -2.35  SIZE   -2.34  -2.35  -2.34
 0.0050  0.00   0.01   0.01   0.01   0.00   0.00  SIZE    0.01   0.00   0.00
-0.0050  0.00  -0.01  -0.01  -0.01   0.00   0.00  SIZE    0.00  -0.01   0.00
 2.3400  2.34   2.34   2.34   2.34   2.34   2.34  SIZE    2.34   2.34   2.34
 9.9950  9.99  SIZE   SIZE   SIZE   SIZE    9.99  SIZE   SIZE    9.99   9.99
PROHIBITED, 2.34 from S9V99    :  2.34
PROHIBITED, 2.3400, no clause  :  0.00

The column headings are short for: no ROUNDED; ROUNDED with no mode; AWAY-FROM-ZERO; NEAREST-AWAY-FROM-ZERO; NEAREST-EVEN; NEAREST-TOWARD-ZERO; PROHIBITED; TOWARD-GREATER; TOWARD-LESSER; TRUNCATION. What the table shows:

  • The three nearest modes split only on exact ties. On 2.345 they give 2.35, 2.34 and 2.34; on 2.355 they give 2.36, 2.36 and 2.35. NEAREST-EVEN sides with one neighbour on the first tie and the other on the second, which is why it does not drift over many ties. On the near-ties (2.3449 and 2.3451) all three agree, because the tie rule never fires.
  • AWAY-FROM-ZERO is not a nearest mode. It takes 2.3449 to 2.35: any inexact value moves outward, however small the excess.
  • Only the two TOWARD modes care about the sign. TOWARD-GREATER takes 2.345 to 2.35 and minus 2.345 to minus 2.34; TOWARD-LESSER does the reverse. Every other column is the same with the sign flipped.
  • Plain ROUNDED is NEAREST-AWAY-FROM-ZERO, column for column, and no ROUNDED is TRUNCATION, as the guide says.
  • Rounding alone can cause a size error. 9.995 fits the receiver's one integer digit, but every mode that rounds it up needs 10.00, so the ON SIZE ERROR branch ran. The modes that round it down stored 9.99.
  • PROHIBITED took the size-error branch on every row, including 2.3400, whose two dropped digits are zeros. The two lines after the table show what decided it in this run: the same 2.34 from an item declared with two decimal places stored without complaint, and with no ON SIZE ERROR phrase the receiver was left as it was (0.00) and the run went on with no message. So in GnuCOBOL 3.2 a PROHIBITED store from a source with more decimal places than the receiver is a size error whatever the digits are; code the phrase whenever you use the mode.

We also computed the 108 cells outside the PROHIBITED column with Python's decimal module (ROUND_DOWN for no ROUNDED and TRUNCATION, ROUND_HALF_UP for ROUNDED and NEAREST-AWAY-FROM-ZERO, ROUND_UP for AWAY-FROM-ZERO, ROUND_HALF_EVEN, ROUND_HALF_DOWN for NEAREST-TOWARD-ZERO, ROUND_CEILING and ROUND_FLOOR for the two TOWARD modes, and a size check against 9.99). All 108 agree with the compiled output.

The lesson's own program behaves the same way across dialects, apart from how it prints. Under -std=mf it printed the worked example above byte for byte; under -std=ibm, whose dialect file turns pretty-display off, the same values came out without decimal points:

Total, no ROUNDED   : 0006446
Total, ROUNDED      : 0006447
2.345 default mode  : 235
2.345 NEAREST-EVEN  : 234
2.355 default mode  : 236
2.355 NEAREST-EVEN  : 236
Size error trapped, WS-Small still 005
Untrapped overflow, WS-Small now 345
Divide by zero trapped, WS-Result still 0000000

-std=cobol2014 refused both programs at their first NEAREST-EVEN statement, with code/cobol-rounded-mode-ties.cob:87: error: syntax error, unexpected Identifier for the tie table and code/cobol-compute-rounded.cob:48: error: syntax error, unexpected Identifier for this lesson's listing. The 3.2 word list for that dialect, cobol2014.words, reserves NEAREST-AWAY-FROM-ZERO, NEAREST-TOWARD-ZERO and NEAREST-EVEN-INTERMEDIATE but has no NEAREST-EVEN entry, so in that dialect the word is read as a data name.

Limitations

The programs were run on GnuCOBOL 3.2 only, under its default, -std=ibm, -std=mf and -std=cobol2014 dialects; IBM Enterprise COBOL itself was not run, and its ROUNDED page documents no MODE phrase at all. The untrapped overflow in step 4 relies on standard truncation for a USAGE DISPLAY receiver; for binary receivers GnuCOBOL's binary-truncate configuration option decides whether truncation follows the picture or the binary field size, and the result can differ. The mode table reproduces the guide's examples for an integer receiver; the tie table above exercises every mode, PROHIBITED, TOWARD-GREATER and TOWARD-LESSER included, on a two-decimal receiver only. ON SIZE ERROR is available on ADD, SUBTRACT, MULTIPLY and DIVIDE as well as COMPUTE, but not on MOVE, so the silent truncation described in the MOVE lesson has no equivalent guard. Finally, ROUNDED applies once, on the store; it does not change how the intermediate expression is evaluated, and the number of decimal places kept in that intermediate is a compiler matter that neither guide pins down for every case.

Correction, 29 October 2026: the build command shown before named the source cobol-compute-rounded-on-size-error.cob, and GnuCOBOL 3.2's cobc refuses a source file whose base name is longer than 31 characters ("invalid file base name ... length exceeds maximum"), even with -o, so it failed as printed. The listing is now shipped as cobol-compute-rounded.cob, and the old download address redirects there. The same day we compiled and ran the program with GnuCOBOL 3.2, and its output matched the output shown here line for line, so the page now says that instead of calling the listing desk-checked only.

Update, 30 October 2026: added the compiled tie table for all eight rounding modes, the -std=ibm, -std=mf and -std=cobol2014 results, and the finding on PROHIBITED; the limitations above now say what was and was not run.

Index terms cobol, gnucobol, compute, rounded, on size error