nautechsystems/nautilus_trader · error

Overflow when scaling f64 to fixed-point i128

Error message

Overflow when scaling f64 to fixed-point i128

What it means

f64_to_fixed_i128 converts a float to raw fixed-point i128 by rounding at the requested precision then multiplying by 10^(FIXED_PRECISION - precision). The checked_mul panics with this message when the result exceeds i128 range. It enforces the fixed-point representable-range invariant.

Source

Thrown at crates/model/src/types/fixed.rs:895

///
/// # Panics
///
/// Panics if `precision` exceeds [`FIXED_PRECISION`], or if scaling the rounded value
/// overflows the raw integer range.
#[must_use]
#[expect(
    clippy::cast_precision_loss,
    clippy::cast_possible_truncation,
    reason = "f64 to fixed-point conversion is inherently lossy; callers validate range and finiteness"
)]
pub fn f64_to_fixed_i128(value: f64, precision: u8) -> i128 {
    check_fixed_precision(precision).expect_display(FAILED);
    let pow1 = 10_i128.pow(u32::from(precision));
    let pow2 = 10_i128.pow(u32::from(FIXED_PRECISION - precision));
    let rounded = (value * pow1 as f64).round() as i128;
    rounded
        .checked_mul(pow2)
        .expect("Overflow when scaling f64 to fixed-point i128")
}

/// Converts an `f64` value to a raw fixed-point `u64` representation with a specified precision.
///
/// Callers are expected to validate that `value` is finite and non-negative; non-finite
/// and negative values saturate at the integer bounds during the float-to-integer cast.
///
/// # Panics
///
/// Panics if `precision` exceeds [`FIXED_PRECISION`], or if scaling the rounded value
/// overflows the raw integer range.
#[must_use]
#[expect(
    clippy::cast_precision_loss,
    clippy::cast_possible_truncation,
    clippy::cast_sign_loss,
    reason = "f64 to fixed-point conversion is inherently lossy; callers validate range and finiteness"
)]

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Validate the value is finite and within range before calling (the doc comment already requires callers to do this).
  2. Clamp or reject out-of-range inputs at the data-ingestion boundary.
  3. Use f64_to_fixed_u128 if the value is non-negative and extremely large (within u128 range).

Example fix

// before
let raw = f64_to_fixed_i128(value, precision); // panics if out of range
// after
if !value.is_finite() || value.abs() > i128::MAX as f64 / 10f64.powi(FIXED_PRECISION) {
    return Err(...);
}
let raw = f64_to_fixed_i128(value, precision);
Defensive patterns

Strategy: validation

Validate before calling

const MAX: f64 = i128::MAX as f64 / 10f64.powi(FIXED_PRECISION);
assert!(value.is_finite() && value.abs() <= MAX);
let raw = f64_to_fixed_i128(value, precision);

Type guard

fn fits_i128_fixed(value: f64) -> bool {
    value.is_finite() && value.abs() <= i128::MAX as f64 / 10f64.powi(FIXED_PRECISION)
}

Prevention

When it happens

Trigger: Calling f64_to_fixed_i128(value, precision) where |value| rounded and scaled exceeds i128::MAX/MIN — requires astronomically large values (~1.7e38 at precision 0) or the float cast producing i128::MAX from non-finite input.

Common situations: Feeding unvalidated float data (NaN/inf or huge numbers parsed from files/APIs) into the fixed-point conversion; fuzz or boundary tests.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/1bb159ed14d32f18. Report an issue: GitHub.