Engineering Lab · Numerical correctness
Understanding the original. Reconstructing the logic. Verifying the result.
A standalone COBOL expression evaluator rebuilt in Rust with explicit decimal arithmetic: 1,000 exact-decimal cases with zero failures, 2,000 of 2,000 legacy comparisons within a stated tolerance, and a 13-expression transcript that matches byte for byte.
The same grammar.
A different kind of number.
Left, the original's evaluation of a multiplication on its COMP-2 floating-point stack. Right, the Rust level that replaces it, where every result passes through a bounded decimal context.
Original · COBOLcobcalc.cob
03 EXP-RS COMP-2.
WHEN ('TERM3')
PERFORM 1770-POP-OP
EVALUATE (EXP-OP)
WHEN ('*')
PERFORM 1840-POP-RS
MOVE EXP-RS TO EXP-WORK-RS-A
PERFORM 1840-POP-RS
MOVE EXP-RS TO EXP-WORK-RS-B
COMPUTE EXP-RS = EXP-WORK-RS-B * EXP-WORK-RS-A
PERFORM 1630-PUSH-RS Operands are popped from a COMP-2 stack, which is binary floating point. Precedence lives in the mode transitions.
Reconstruction · Rustsrc/lib.rs
fn term(&mut self) -> Result<Number, Error> {
let mut value = self.power()?;
loop {
if self.take(b'*') {
let rhs = self.power()?;
value = self.finite(value * rhs)?;
} else if self.take(b'/') {
let rhs = self.power()?;
if rhs.is_zero() {
return Err(self.error("division by zero"));
}
value = decimal_math::division(&value, &rhs)
.map_err(|message| self.error(message))?;
} else {
return Ok(value);
}
}
} Each grammar level is a function; every result passes through the bounded decimal context. Division by zero is an error with a column, not a NaN.
Execution flow · 2^3^2select a step
34 significant digits, half-even, with a 50-digit working context for the operations that cannot be exact. src/decimal_math.rs
Recorded demonstration · make demoREADME
| Expression | Rust output |
|---|---|
| 2+3*4 | 14.00000 |
| (2+3)*4 | 20.00000 |
| 2^3^2 | 512.00000 |
| SQRT(144)+1,000/4 | 262.00000 |
| -2^2 | 4.00000 |
| (5/1200*250000*((1+5/1200)^(30*12)))/(((1+5/1200)^(30*12))-1) | 1342.05405 |
Recorded from the published Rust binary in the original's five-place display. The last line is the original author's mortgage-payment example. 0.1+0.2 prints 0.3, where the original prints 0.29999: the one intentional difference, documented in MIGRATION.md.
01 / THE CASE
Why a calculator belongs in the Lab.
Precedence, right-associative powers, signed literals that bind before powers, commas inside integers, a square root, errors with a column number: COBCALC is small enough to read in an afternoon and complete enough to carry every question a numerical migration raises. Above all it carries the question of the number type, because the original keeps its result stack in COMP-2, which is binary floating point.
The Rust reconstruction keeps the grammar and changes the arithmetic deliberately: 34 significant digits, half-even, with a 50-digit working context for the operations that cannot be exact. 0.1 + 0.2 is 0.3.
02 / TWO STANDARDS
Two standards, never confused.
Because the original is floating point, agreement with it can only ever be agreement within a tolerance. So the Lab publishes two separate results. The exact-decimal contract: 1,000 deterministic cases compared as decimals with an independent reference, zero tolerance, zero failures. Legacy compatibility: 2,000 expressions run against the real GnuCOBOL original within an absolute and relative tolerance of 2e-12, 2,000 passing, largest observed difference about 1e-7. A selected 13-expression session additionally matches the original transcript byte for byte.
The second result is numerical compatibility. It is not a financial-equivalence claim, and the repository says so in its own verification file.
03 / BOUNDARY
The boundary of the claim.
An algebraic evaluator, not a banking, tax or payroll engine. Migrating such a system also requires each copybook's exact scale, receiving-field rounding policy, size-error behaviour and record encodings, and those must be verified separately and compared byte for byte. Choosing a decimal library proves none of them on its own.
Inspect the evidence.
Each view answers one question: what was shown, how, what Genyra built, the numbers, what has not been established, where the code is and how to run the same check yourself.
Understanding the original. Reconstructing the logic. Verifying the result.
A standalone COBOL algebraic expression evaluator rebuilt in Rust with explicit decimal arithmetic, and tested two ways: exactly against a decimal reference, and within a stated tolerance against the floating-point original.
Original work: Manny Juan, GPL-3.0-or-later, retained. Rust implementation and evidence: Genyra LLC. Status: Verified evaluator · Rust 1.1.0.
Two separate comparisons that are never combined: 1,000 deterministic cases computed independently with a decimal reference must match exactly; 2,000 expressions run against the real GnuCOBOL original must agree within 2e-12, because the original computes in binary floating point.
Standards used on this project
- Exact-decimal reference test
- Tolerance-based numerical compatibility
- Exact byte-for-byte comparison
- Functional tests
- CI build success
Recorded test conditions
- Arithmetic: BigDecimal: 34 significant digits, half-even, 50-digit working precision.
- cobc (GnuCOBOL) 3.2.0; rustc 1.99.1; corpus seed 20261103.
- Observation-only change to a copy of the original: DISPLAY 'RAW=' EXP-RS immediately before MOVE EXP-RS TO WRK-RESULT; removing it reproduces the original transcript exactly.
- The original COMP-2 program is a floating-point compatibility oracle, not an exact-financial-arithmetic oracle.
What Genyra engineered
- A bounded recursive-descent evaluator for the complete grammar: + - * / ^, parentheses, SQRT, signed numbers and commas in integer parts, with right-associative powers as in the COBOL source.
- An explicit decimal contract: 34 significant digits, half-even, with 50-digit working precision for division, roots and fractional powers. No f32 or f64 anywhere.
- Errors with column numbers instead of silent wraps or infinities; range limits on integer digits, scale and exponents.
- Vendored dependencies for offline builds, native tests including 6,000 malformed inputs, and a reproducible comparison harness.
Source passages quoted on this page
- cobcalc.cob · The original: a mode machine on a floating-point stack
- src/lib.rs · The reconstruction: precedence as functions, numbers as decimals
- src/decimal_math.rs · The decimal context
Figures are read from the repository's evidence file at build time; every label states which standard produced it. Figures of different standards are never combined.
- The floating-point comparison is numerical compatibility within a stated tolerance; it is not a zero-tolerance financial-equivalence claim and must never be read as one.
- An intentional improvement breaks identical text in one case: 0.1 + 0.2 is now exactly 0.3, where the original displays 0.29999.
- Repeating fractions and irrational results are decimal approximations; decimal storage cannot make them exact. Fractional powers use a bounded series approximation.
- An algebraic evaluator, not a banking, tax or payroll engine. A financial migration must add each copybook's scale, rounding points and overflow policy and verify them separately.
The full list is in the published report.
- Genyra repository · COBCALC, Rust implementation and harness
- Original project · COBCALC by Manny Juan, GPL-3.0-or-later, retained
- Verification evidence · machine-readable results
- Report · decisions, conditions and limitations
- Upstream revision ·
d3d3d20d3021 - GitHub Actions · the same verification on every push
Latest verification run passed · 2026-11-03 · a108e07· run
git clone https://github.com/GenyraLLC/cobcalc-rust.git cd cobcalc-rust cargo build --release --offline --locked ./target/release/cobcalc --eval '2^3^2' # 512.000000000000 make verify # needs Python 3.10+ and GnuCOBOL 3.1+
make verify checks the SHA-256 of the pinned original, builds and runs it with bounds checks, and compares it with the Rust program; every case is also checked against independently computed expected values. Instructions in the repository.
- Expression parsing
- Operator precedence
- Source-language behaviour
- Decimal arithmetic
- Explicit precision
- Rounding rules
- Error handling
- Equivalence testing
Questions we are asked.
Short answers in the same terms as the page. Anything specific to your systems is agreed by enquiry.
- Why not make the Rust version match the COBOL output exactly?
- Because the original's output is sometimes wrong in the way binary floating point is wrong: it displays 0.29999 for 0.1 + 0.2. Reproducing that would preserve an artefact rather than a rule. The reconstruction states a decimal contract instead, and measures its distance from the original honestly, within a published tolerance.
- Is there a live calculator on this page?
- No. The outputs shown are recorded from the published Rust binary and the verification transcripts. A browser calculator would have to implement the documented grammar and numeric rules faithfully to be worth showing; until it does, recorded demonstrations are the honest choice.
Discuss a reconstruction
From an unanswered question
to an engineered possibility.

