nautechsystems/nautilus_trader · error

Overflow when scaling f64 to fixed-point u64

Error message

Overflow when scaling f64 to fixed-point u64

What it means

f64_to_fixed_u64 converts a non-negative float to raw fixed-point u64, scaling the rounded value by 10^(FIXED_PRECISION - precision). The checked_mul panics with this message when the scaled value exceeds u64::MAX. Callers must ensure the value is finite and non-negative and within range.

Source

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

/// # 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"
)]
pub fn f64_to_fixed_u64(value: f64, precision: u8) -> u64 {
    check_fixed_precision(precision).expect_display(FAILED);
    let pow1 = 10_u64.pow(u32::from(precision));
    let pow2 = 10_u64.pow(u32::from(FIXED_PRECISION - precision));
    let rounded = (value * pow1 as f64).round() as u64;
    rounded
        .checked_mul(pow2)
        .expect("Overflow when scaling f64 to fixed-point u64")
}

/// Converts an `f64` value to a raw fixed-point `u128` 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. Pre-check that value * 10^FIXED_PRECISION fits in u64 and reject or clamp otherwise.
  2. Use f64_to_fixed_u128 for larger magnitudes.
  3. Route values that can be negative through the signed variant f64_to_fixed_i64 instead.

Example fix

// before
let raw = f64_to_fixed_u64(huge_value, precision); // panics
// after
let max = u64::MAX as f64 / 10f64.powi(FIXED_PRECISION);
assert!(value.is_finite() && value >= 0.0 && value <= max);
let raw = f64_to_fixed_u64(value, precision);
Defensive patterns

Strategy: validation

Validate before calling

const MAX: f64 = u64::MAX as f64 / 10f64.powi(FIXED_PRECISION);
assert!(value.is_finite() && (0.0..=MAX).contains(&value));
let raw = f64_to_fixed_u64(value, precision);

Type guard

fn fits_u64_fixed(value: f64) -> bool {
    value.is_finite() && (0.0..=u64::MAX as f64 / 10f64.powi(FIXED_PRECISION)).contains(&value)
}

Prevention

When it happens

Trigger: Calling f64_to_fixed_u64 with a value whose scaled magnitude exceeds u64::MAX (~1.8e19 at precision 0); the dedicated test test_f64_to_fixed_u64_overflow_panics exercises exactly this.

Common situations: Large unsigned quantities from feeds or computed values (e.g. cumulative volumes in base units), or accidental negative/infinite values cast to u64 before the multiplication overflow check.

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/7d0a8ab5e8baf546. Report an issue: GitHub.