COOLJAPAN
← All posts

OxiNum 0.1.4 Released — When a Perfectly Valid Number Tried to Allocate Past Its Budget

OxiNum 0.1.4 closes an unbounded-allocation class across add/sub, remainder, and binary-splitting transcendentals — a legitimately valid but extreme BigFloat exponent could previously drive memory proportional to the exponent gap, aborting the process. New MAX_EXACT_CONVERSION_BITS-guarded try_* fallible APIs, an enforced BigFloat exponent range (EMAX/EMIN, ilogb), a fixed IEEE-754 subnormal-rounding double-rounding bug, and a Rem correctness fix. Pure Rust, no GMP, no MPFR, no FFI.

release oxinum pure-rust cooljapan noffi arbitrary-precision bignum correctness denial-of-service

A value doesn’t have to be attacker-controlled to be dangerous. 2^-1_000_000_000 is a perfectly legitimate BigFloat — and asking this crate to lift it to an exact rational used to try to allocate a denominator a billion bits wide.

Today we released OxiNum 0.1.4 — the COOLJAPAN Pure-Rust arbitrary-precision math layer: bignum integers, exact rationals, high-precision floats, and arbitrary-precision complex numbers. This release closes a whole class of unbounded-allocation bugs that a legitimately valid, merely extreme, value could trigger — no adversarial input required, just a number far enough from zero or from one.

No GMP. No MPFR. No rug. No FFI, no -sys crates, no build-time C toolchain. Just clean, memory-safe Rust that compiles to a single static binary and goes wherever Rust goes.

Why OxiNum 0.1.4 is a game changer

BigFloat stores an unbounded i64 exponent alongside its mantissa — which is exactly the design that let a valid value turn a routine operation into a process-ending allocation:

None of this needed malicious input. A single valid BigFloat at an extreme-but-legal exponent was enough.

OxiNum 0.1.4 closes all of it with one governing idea — MAX_EXACT_CONVERSION_BITS (2^27 bits), a budget every exact-conversion path now checks before it allocates:

Technical Deep Dive: two correctness fixes underneath the hardening

  1. to_f64 double-rounding, fixed. A BigFloat that was itself already the rounded result of an earlier operation got re-rounded blind on conversion to f64 — and ties-to-even then resolved a tie the un-rounded value never actually sat on. That’s a 1-ULP error in roughly a quarter of all quotients landing in [2^-1023, 2^-1022), where the 52-bit subnormal grid’s halfway points coincide with the 53-bit normal grid’s. BigFloat now records MPFR’s ternary value (the direction of its last rounding) and to_f64 breaks exact ties with it — div_ref(..).to_f64() and (a * b).to_f64() now agree with the hardware bit for bit, all the way down to 2^-1074.
  2. Subnormal flush-to-zero, fixed. Every magnitude below 2^-1074 used to flush straight to 0.0 instead of rounding onto the subnormal grid — but IEEE 754 requires gradual underflow, and a value in (2^-1075, 2^-1074) is above half the smallest subnormal and owes 2^-1074, not zero. to_f64 now rounds once, directly from the exact stored value, correctly handling the overflow threshold at the exact IEEE halfway point and preserving -0.0 on negative underflow.
  3. Rem correctness. rem_core rounded the quotient to prec significant bits before truncating, which could produce a remainder outside [0, |b|) whenever |a/b|’s integer part needed more bits than prec. It now works directly on exact mantissas.

Getting Started

[dependencies]
oxinum = "0.1.5"
use oxinum_core::{OxiNumError, Sign};
use oxinum_float::native::{BigFloat, RoundingMode};
use oxinum_int::native::BigUint;

// One bit past EMAX is refused rather than silently accepted --
// try_from_parts is the fallible constructor introduced this release.
let err = BigFloat::try_from_parts(
    Sign::Positive,
    BigUint::one(),
    BigFloat::EMAX + 1,
    53,
    RoundingMode::HalfEven,
)
.expect_err("2^(EMAX+1) is out of range");
assert!(matches!(err, OxiNumError::Overflow(_)));

// Non-panicking integer conversion, guarded by MAX_EXACT_CONVERSION_BITS.
let x = BigFloat::from_f64(1.5);
match x.try_to_bigint_round() {
    Ok(n) => println!("{n}"),
    Err(e) => println!("conversion budget exceeded: {e:?}"),
}

What’s New in 0.1.4

Tips

This is the foundation

Correct, allocation-bounded arbitrary-precision arithmetic matters most for whatever builds numerically on top without wanting to think about denial-of-service from its own math library. SciRS2 pins the full oxinum-* family as its Pure-Rust GMP/MPFR-free arbitrary-precision layer.

Repository: https://github.com/cool-japan/oxinum

Star the repo if “a valid number shouldn’t be able to abort your process” is a bar every bignum library should clear.

The era of allocation size scaling with an untrusted (or merely extreme) exponent is over. Pure Rust arbitrary-precision math that’s fast, safe, and sovereign — is here.

KitaSan at COOLJAPAN OÜ August 6, 2026

↑ Back to all posts