Guide

COBOL to Java: the semantics that break.

The COBOL behaviours that translated code most often gets wrong, and how to check each one.

In this guide7 parts
  1. 01Packed decimal and PIC scaling
  2. 02ROUNDED, truncation and ON SIZE ERROR
  3. 03REDEFINES and group moves
  4. 04PERFORM THRU, GO TO and fall-through
  5. 05EBCDIC sort order and character data
  6. 06File I/O and FILE STATUS
  7. 07How to check each one

In short

  • Most COBOL-to-Java bugs come from semantics, not syntax: decimal arithmetic, truncation, overlaid storage, control flow and character encoding.
  • Java's double must never stand in for a COBOL numeric field; use BigDecimal with the matching scale and rounding.
  • Each pitfall below needs a specific test, ideally replayed against recorded runs of the original program.

Packed decimal and PIC scaling

COBOL numeric fields have a fixed number of digits and decimal places set by their PIC clause, and many are stored as packed decimal (COMP-3). Java's double is binary floating point and cannot represent most decimal amounts exactly, so totals drift.

Use BigDecimal with the field's exact scale, and apply the same truncation COBOL does when a value is moved into a smaller field.

A PIC S9(7)V99 COMP-3 amount in Java
// COBOL: 05 WS-AMOUNT PIC S9(7)V99 COMP-3.
BigDecimal amount = new BigDecimal("1201.50").setScale(2, RoundingMode.DOWN);

ROUNDED, truncation and ON SIZE ERROR

Without ROUNDED, COBOL truncates the extra digits; with it, it rounds. When a result has more integer digits than the receiving field, COBOL silently drops the high-order digits unless ON SIZE ERROR is coded. A translation that rounds everywhere, or throws on overflow, changes results.

REDEFINES and group moves

REDEFINES lets two layouts share the same bytes, and group moves copy raw storage between records. An idiomatic object model loses that byte-level relationship. Translations need a byte-accurate view of the record wherever a REDEFINES or group move is in play.

PERFORM THRU, GO TO and fall-through

PERFORM A THRU B runs every paragraph between A and B, GO TO jumps across them, and execution falls from one paragraph into the next unless something stops it. Translating each paragraph into a separate method without preserving that flow changes what runs.

EBCDIC sort order and character data

Mainframes use EBCDIC, where letters sort before digits; ASCII and Unicode sort digits first. Sorts, range checks and key comparisons can silently change order after migration.

File I/O and FILE STATUS

COBOL programs branch on FILE STATUS codes after every read and write, and VSAM files have keyed access with specific semantics. The translated I/O layer has to return the same statuses in the same situations, or error-handling paths change.

How to check each one

Each of these is testable. Replay recorded runs of the original program against the translation, compare with exact decimals, and add boundary values for every numeric field. Reading the code is not enough; running it against the original is what catches them. That loop is what Montora runs on every change. For the full checklist, see how to test a COBOL migration.

Questions

What is COMP-3 in COBOL?

COMP-3 is packed decimal storage: two decimal digits per byte with the sign in the last half-byte. It stores exact decimal values, so in Java it should map to BigDecimal, never double.

Why does COBOL to Java conversion produce different results?

Usually because of semantics the translation didn't preserve: decimal scale and truncation, ROUNDED behaviour, REDEFINES storage, paragraph fall-through or EBCDIC sort order.

How do you convert COBOL decimals to Java?

Use BigDecimal with the scale from the PIC clause and the same rounding mode COBOL applies: truncation by default, rounding only where ROUNDED is coded.

Keep reading

See it on your own code.

Montora is taking on a small number of design partners: banks, insurers and other teams with COBOL they can't afford to get wrong.