Types phase 2: economy/idle number correctness — decimal + bigint #52

Closed
opened 2026-08-31 17:30:49 +02:00 by orkun · 1 comment
Owner

Follow-up to #1 (proposal), phase 2 of the phasing.

decimal — a 128-bit base-10 exact number (~28-29 significant digits), like C# decimal, for tycoon/economy money: prices, balances and taxes that must add up exactly with no binary rounding on values like 0.10. Exact, so deterministic.

bigint — an arbitrary-precision integer with no upper bound, like C# BigInteger / Go math/big.Int, for idle counters, exact huge currencies, score hashing and deterministic RNG streams. Exact, so deterministic (slower).

Both are the correct answer for money and exact large counts — prefer them over fixed when the domain is currency, not physics. Neither introduces binary floating point.

Phase 1 (IVec2 + Rect, alongside the pre-existing Vector and Color) shipped in the #1 thread.

Follow-up to #1 (proposal), phase 2 of the phasing. **decimal** — a 128-bit base-10 exact number (~28-29 significant digits), like C# `decimal`, for tycoon/economy money: prices, balances and taxes that must add up exactly with no binary rounding on values like 0.10. Exact, so deterministic. **bigint** — an arbitrary-precision integer with no upper bound, like C# `BigInteger` / Go `math/big.Int`, for idle counters, exact huge currencies, score hashing and deterministic RNG streams. Exact, so deterministic (slower). Both are the correct answer for money and exact large counts — prefer them over `fixed` when the domain is currency, not physics. Neither introduces binary floating point. Phase 1 (IVec2 + Rect, alongside the pre-existing Vector and Color) shipped in the #1 thread.
orkun added the
proposal
priority:medium
area:types
labels 2026-08-31 17:30:49 +02:00
Author
Owner

Shipped — BigInt + Decimal (bd6b12d)

Types phase 2, the "money problem", is done. A self-contained bignum engine (runtime/native/bignum.ludic, only compiler intrinsics) is spliced in on demand and exposed as two namespaces:

  • BigInt.* — arbitrary-precision integer, sign-magnitude with base-1e9 limbs. from / parse, add / sub / mul / pow, integer div / mod, cmp / eq / is_zero, to_int, str. For idle/incremental counters and exact huge currencies that overflow a 32- or 64-bit int (e.g. BigInt.pow(BigInt.from(2), 100) = 1267650600228229401496703205376).
  • Decimal.* — exact base-10 fixed-point (a BigInt mantissa + a decimal scale). from / parse, exact add / sub / mul, cmp / eq, scale / rescale (truncate toward zero), str. So the classic bug is gone: Decimal.add(Decimal.parse("0.10"), Decimal.parse("0.20")) is exactly "0.30", and 1.5 == 1.50.

Both are exact and therefore deterministic — no f32/f64 anywhere.

Delivered following the Regex/Value precedent: the value type is the runtime struct (a ptr), reached through the namespace and type inference, so no new type keyword was needed. Division that rounds (full bignum/bignum division and Decimal.div) is deliberately out of scope for this phase — BigInt.div/mod by an int and Decimal.rescale cover the exact cases; a rounding-division API can follow if needed.

Wired end to end: parser splice trigger (g_uses_bignum), emit_call dispatch, reseeded C-free seed, a self-asserting example (examples/library/bignum.ludic, 16 assertions, in the regression suite), and per-symbol docs + section + inventory. All suites green: x test 82/82, x selfhost-test 30/30 (golden renders byte-identical, bootstrap fixpoint holds), x test-tools 30/30, check-impl/check-vocabulary/check-docs.

Closing phase 2. Phases 3-5 remain in #53 / #54 / #55.

## Shipped — `BigInt` + `Decimal` (bd6b12d) Types phase 2, the "money problem", is done. A self-contained bignum engine (`runtime/native/bignum.ludic`, only compiler intrinsics) is spliced in on demand and exposed as two namespaces: - **`BigInt.*`** — arbitrary-precision integer, sign-magnitude with base-1e9 limbs. `from` / `parse`, `add` / `sub` / `mul` / `pow`, integer `div` / `mod`, `cmp` / `eq` / `is_zero`, `to_int`, `str`. For idle/incremental counters and exact huge currencies that overflow a 32- or 64-bit int (e.g. `BigInt.pow(BigInt.from(2), 100)` = `1267650600228229401496703205376`). - **`Decimal.*`** — exact base-10 fixed-point (a `BigInt` mantissa + a decimal scale). `from` / `parse`, exact `add` / `sub` / `mul`, `cmp` / `eq`, `scale` / `rescale` (truncate toward zero), `str`. So the classic bug is gone: `Decimal.add(Decimal.parse("0.10"), Decimal.parse("0.20"))` is exactly `"0.30"`, and `1.5 == 1.50`. Both are exact and therefore deterministic — no `f32`/`f64` anywhere. Delivered following the Regex/Value precedent: the value type is the runtime struct (a `ptr`), reached through the namespace and type inference, so no new type keyword was needed. Division that rounds (full bignum/bignum division and `Decimal.div`) is deliberately out of scope for this phase — `BigInt.div`/`mod` by an `int` and `Decimal.rescale` cover the exact cases; a rounding-division API can follow if needed. Wired end to end: parser splice trigger (`g_uses_bignum`), `emit_call` dispatch, reseeded C-free seed, a self-asserting example (`examples/library/bignum.ludic`, 16 assertions, in the regression suite), and per-symbol docs + section + inventory. All suites green: `x test` 82/82, `x selfhost-test` 30/30 (golden renders byte-identical, bootstrap fixpoint holds), `x test-tools` 30/30, `check-impl`/`check-vocabulary`/`check-docs`. Closing phase 2. Phases 3-5 remain in #53 / #54 / #55.
orkun closed this issue 2026-08-31 17:45:32 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: workshopsoft/ludic#52
No description provided.