The pitch sounded simple: take a well-known open-source library, port it to a new language, prove it behaves identically.
Port Mortem
- 72 hours.
I worked alone.
At kickoff, the organizers released a curated pool of 100 eligible open-source repositories for participants to choose from.
Most people went for something manageable.
I scrolled the list until I found the one that scared me the most.
This is the story of what I built, what broke me, how I proved correctness, and the bugs I didn't expect to find.
What I Picked From the List — And Why It Was Probably Stupid is a JavaScript library for arbitrary-precision decimal arithmetic.
Not a toy.
Not a utility.
A full numeric engine: Arbitrary precision up to 1 billion significant digits 9 rounding modes Full arithmetic: add, subtract, multiply, divide, modulo, power Transcendental functions: , , , , Full trigonometry: , , , , , , , , , , , , NaN, ±Infinity, signed zero (-0), hex/binary/octal input The whole thing ships with zero dependencies The target: Go.
Track F (JavaScript → Go).
Out of every repo in the recommended pool — compression libraries, slug generators, cron parsers, JSON parsers — this was the one with full trig, full transcendentals, 9 rounding modes, and arbitrary precision.
I knew it would be hard. alone ended up 1,415 lines of dense numeric algorithms.
But the thing that made it genuinely interesting wasn't the math — it was the philosophy question I kept bumping into: When the original has a bug, do you fix it or port it?
The Philosophy: Faithfulness Over Correctness Most people porting a library think their job is to produce correct output.
I think that's wrong.
My job was to produce identical output — including the wrong ones.
Here's why: if you're a JavaScript developer who has running in production for three years, your code is already handling its edge cases.
Your tests are written against its behavior.
Your financial calculations depend on its specific rounding at boundaries.
If I "fix" a bug while porting, your code breaks when you switch to my library.
The port becomes untrustworthy.
So I made a decision early: behavioral parity is the product.
Every quirk gets preserved.
Every weird edge case gets matched.
Every bug gets inherited — and documented.
This turned out to matter more than I expected, because I found five — three bugs in my own Go port, where JavaScript's permissive semantics silently tolerated mistakes that Go refused to, and two genuine upstream bugs in itself, discovered only through differential fuzzing.
Proving Parity — 1,518 Lines, Byte for Byte Before I get to the bugs, let me explain how I know I actually have parity.
I built — a cross-validation harness that runs a shared corpus through both the Go port and the live library side-by-side: The corpus covers: signs, zeros (, ), small and large exponents crossing the / formatting boundaries, full integer precision beyond , subnormals, , . 66 inputs × 23 operations = 1,518 result lines.
All 1,518 are byte-for-byte identical between Go and .
But cross-validation alone isn't enough.
I also ported all 61 decimal.js test modules as white-box Go tests — every assertion kept, same order, same values.
That's where the real bugs surfaced.
Bug #1: — int64 Overflow in While porting the rounding internals, I hit a case where the ported test suite produced garbage digits — and in some configurations, an out-of-bounds access that could panic or wedge the test run.
The problem was in , the function that rounds a result to the configured precision.
When the rounding digit lives deep inside a base-1e7 word, it calls to extract it.
The original decimal.js computes this in JS numbers, where the intermediate result is always safe.
In Go with , specific values caused the computation to overflow — producing garbage digits, or worse, hitting an index that didn't exist.
The fix: now clamps the exponent before division.
But the important part is what I did after fixing it: That test is now permanent.
If anyone ever refactors , the overflow cannot silently come back.
Bug #2: — Slice Out of Bounds Raising a negative base to an integer exponent requires checking whether the exponent is odd or even.
The original JS code does this: In JavaScript, reading past the end of an array returns , and .
Silently.
No error.
In Go, the same index goes out of bounds and panics.
I added a helper that returns 0 for out-of-range indices — matching JavaScript's implicit behavior — and locked the fix in: Bug #3: — When My Port Disagreed With the Reference The ECMAScript spec (§15.8.2.13) is clear: should be .
Go's returns .
So does... the reference?
No — this is the one where I got it backwards at first.
Let me be precise about what actually happened. itself returns for and — correct, spec-compliant.
My port's wraps Go's , which returns for these inputs, so my first version diverged from the reference.
The bug was in my port, not in the original.
The fix wraps so that a base of with an infinite exponent produces , matching both and ECMAScript: This is the honest version of the story: returns , the reference and the port both return , and the regression test locks the parity in.
Bug #4: — The One Differential Fuzzing Found This is the most interesting one, and the only one found by fuzzing rather than porting.
I ran the port against (Python's arbitrary-precision math library) at 200+ digits of precision.
Most results matched.
One didn't. returns for regardless of what is.
But the correct answer depends on the base: So should return .
Both and return .
The root cause is a short-circuit in decimal.js's : The base is never consulted when the argument is zero.
The practical impact: any computation using probabilities as logarithm bases (information theory, entropy calculations) where the argument can reach zero will silently get the wrong sign.
I pinned the parity behavior in a regression test and documented when we'd fix it: if upstream ships a correction, we follow.
Bug #5: — An Infinite Loop Under The continued-fraction expansion in exits when the denominator grows past .
Under (rounding mode 3), an exact remainder is (IEEE-correct: x−x → −0 under round-toward-negative).
On the next iteration that divisor makes the quotient instead of , so the new denominator is , and is — the loop doesn't break.
From there the values collapse to , and is always too.
The loop never terminates.
Both and hang identically.
Only triggers it.
All other modes produce as the exact remainder, which terminates the loop cleanly.
It's not a crash — it's a denial of service.
I documented it in and chose not to fix it.
Parity is the contract.
The Edge Case That Actually Ate Six Hours None of the above were the hardest thing.
The hardest thing was concurrency.
JavaScript is single-threaded. stores three mutable flags — , , and — as module-level globals.
In Node.js, this works fine: only one computation runs at a time.
In Go, multiple goroutines can call operations concurrently.
Module-level mutable state is a real data race.
Here's what found when I first ran it: The fix required moving , , and out of package-level variables and onto the struct — the per-clone state that mirrors decimal.js's own guidance to give each concurrent context its own constructor ( creates an isolated configuration).
But I had to actually redesign the internal call graph to thread the constructor through every nested operation.
Then I wrote to prove it: Zero races.
Race detector clean.
The JS library's docs tell you to give each concurrent context its own constructor ( creates one with isolated configuration) precisely because the module-level flags exist.
My port makes that model structural: the flags live on the , so one clone per goroutine is race-clean — verified by 64 concurrent cloned constructors under — whereas decimal.js's module globals would race if you tried the same thing in a worker pool.
The Test Inventory (Because Coverage Claims Are Cheap) I'm tired of ports that say "100% test pass" when t