math.round-div-big
Divide two integers of any size with an explicit rounding mode, on decimal strings, for exact money sums past 2^53.
1.0.0 · published 2026-10-03 by charlie · Anterra
Pinned by 21 tests, run in TypeScript, Python and Rust.
What it does
`math.round-div` for integers of any size. The numerator and denominator are decimal strings (the form `math.big-integer` uses), and so is the answer: `roundDivBig("9007199254740993", "2", "down")` is `"4503599627370496"`.
## Why it exists
For example
round_div_big(7, 2, half-up)→ 4 a half rounds away from zero under half-upround_div_big(-7, 2, half-up)→ -4 a negative half rounds away from zero too, as math.round-div doesround_div_big(5, 2, half-even)→ 2 half-even rounds 2.5 down to the even 2
The function
The same function in TypeScript, Python and Rust, pinned by the same tests. Pick your language; the choice follows you around the registry.
def round_div_big(numerator: str, denominator: str, mode: RoundingMode) -> str
| numerator | string | a whole number in decimal: optional "-", digits, no leading zeros |
| denominator | string | a whole number in decimal, not zero |
| mode | RoundingMode | half-up and half-even round ties away from zero and to even; down and up are towards and away from zero |
| returns | string | the rounded quotient in the same decimal form |
Your code names it in one line, in the file that uses it
from fune.math.round_div_big import round_div_big # math.round-div-big@^1
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from .math_big_integer import parse_big_integer ← from math.big-integer ^1.0.0 · built alongside by fune
from .math_round_div import RoundingMode, round_div ← from math.round-div ^1.0.0 · built alongside by fune
def round_div_big(numerator: str, denominator: str, mode: RoundingMode) -> str:
"""math.round-div, for integers too big for a JavaScript number.
Python's integers never overflow, so this is math.round-div itself; the
capability exists so TypeScript and Rust, whose native integers stop at
2^53 and 2^127, give the same answer from the same decimal strings.
"""
n = parse_big_integer(numerator)
d = parse_big_integer(denominator)
if mode not in ("half-up", "half-even", "down", "up"):
raise ValueError('unknown rounding mode "%s"' % (mode,))
return str(round_div(n, d, mode))Install
fune build
With that line in your source, in a Python project (language python in fune.project), fune build resolves it and its 2 dependencies, pins them in fune.lock, downloads only the Python package of each, and builds the code above into your project’s .fune/build, one readable file per capability with a header linking back here. Or pin a range in fune.project and build in one step:
fune add math.round-div-big
The manifest, vectors and README with only the Python implementation. Install it without the registry with fune add ./math.round-div-big-1.0.0-python.fune, or fetch it from a terminal with fune pull math.round-div-big@1.0.0:python.
The whole function, every language, is one file too: math.round-div-big-1.0.0.fune, 10,443 bytes, sha256 84e7151555e35d220724d00e6c32107dde025c9fa1ca43935b7e774824f035d7. It installs into a project of any language.
Customise it in your app
The seams this capability offers. Put a marker directly above a function of your own and fune build wires it into the built code; the package on the registry is not changed, the built file’s header lists it under CUSTOMISED, and fune hooks lists every hook in the project. How hooks work.
before — your function gets the arguments and returns them, changed or not, or throws to refuse the call.
# fune: before math.round-div-big
after — your function gets the result and the arguments, and returns the final result.
# fune: after math.round-div-big
replace — inside this capability’s code only, calls to a dependency go to your function, with the same signature. Other capabilities that use it are unaffected; write in * to replace it everywhere.
# fune: replace math.big-integer in math.round-div-big
# fune: replace math.round-div in math.round-div-big
step — your function runs at a numbered point inside the function’s body, receives the in-scope values it names as parameters, and may return replacements. List the points with fune show math.round-div-big --steps.
# fune: step math.round-div-big after <n|label>
Tests
A version published now needs at least 8 tests for every function, and one that expects the error for each function that throws; the registry refuses it otherwise. fune verify --all runs each case in TypeScript, Python and Rust, and a project runs them again with fune verify. This page lists the cases; it does not run them. The exact JSON is vectors.json.
| Case | Arguments | Expected | |
|---|---|---|---|
| a half rounds away from zero under half-up | 7, 2, half-up | → | 4 |
| a negative half rounds away from zero too, as math.round-div does | -7, 2, half-up | → | -4 |
| half-even rounds 2.5 down to the even 2 | 5, 2, half-even | → | 2 |
| half-even rounds 3.5 up to the even 4 | 7, 2, half-even | → | 4 |
| 2^53 + 1 halved, down: a JavaScript number would already have lost the 1 | 9007199254740993, 2, down | → | 4503599627370496 |
| 2^53 + 1 halved, half-up | 9007199254740993, 2, half-up | → | 4503599627370497 |
| thirty digits divided by a thousand, half-up | 123456789012345678901234567890, 1000, half-up | → | 123456789012345678901234568 |
| thirty negative digits divided by a thousand, down is towards zero | -123456789012345678901234567890, 1000, down | → | -123456789012345678901234567 |
| up rounds any remainder away from zero | 1, 3, up | → | 1 |
| up on a negative is away from zero as well | -1, 3, up | → | -1 |
Show the other 11 tests
| Case | Arguments | Expected | |
|---|---|---|---|
| a small negative that rounds to zero is 0, never -0 | -1, 3, half-up | → | 0 |
| a negative denominator makes the quotient negative | 10, -4, half-up | → | -3 |
| zero divided by anything is zero | 0, 5, up | → | 0 |
| an exact tie beyond 64 bits goes to even under half-even | 25000000000000000000, 10000000000000000000, half-even | → | 2 |
| the next tie beyond 64 bits goes up to even | 35000000000000000000, 10000000000000000000, half-even | → | 4 |
| exact division has nothing to round | 100000000000000000000, 4, up | → | 25000000000000000000 |
| a zero denominator is an error | 1, 0, half-up | → | error: denominator must not be zero |
| a decimal point is not a whole number | 1.5, 2, half-up | → | error: not a whole number in decimal |
| a leading zero is refused | 07, 2, half-up | → | error: not a whole number in decimal |
| a number instead of a string is refused | 7, 2, half-up | → | error: not a whole number in decimal |
| an unknown mode is an error | 7, 2, sideways | → | error: unknown rounding mode |
More from the author
Exact money arithmetic multiplies before it divides: litres from a distance and a fuel economy, times a price in tenths of a penny, or a value retained through several years of depreciation at basis-point rates. The product passes 2^53 quickly, and `math.round-div` takes a JavaScript `number` (and a Rust `i64`), which silently loses digits past that point. Doing the one rounding step here keeps the answer exact and identical in all three languages.
## Rounding
Exactly math.round-div's modes, applied to the magnitude with the sign put back afterwards:
- `half-up`: ties away from zero (7/2 is 4, -7/2 is -4); - `half-even`: ties to the even neighbour (5/2 is 2, 7/2 is 4); - `down`: towards zero; - `up`: away from zero.
A negative quotient that rounds to nothing is `"0"`, never `"-0"`.
## Input form
Canonical decimal integers only, as `math.big-integer` parses them: an optional `-`, then digits, no leading zeros, no `+`, no point, no spaces. Anything else is an error, as is a zero denominator or an unknown mode.
Files
| Path | Bytes |
|---|---|
| README.md | 1,294 |
| impl/python.py | 722 |
| impl/rust.rs | 2,020 |
| impl/typescript.ts | 1,387 |
| vectors.json | 2,682 |