databendlabs/databend · error

Multiplication resulted in NaN

Error message

Multiplication resulted in NaN

What it means

The Mul impl for NotNan<T> multiplies self.0 by a raw T and passes the product through NotNan::new, which panics with this message if the product is NaN. Multiplication yields NaN from 0 * inf, 0 * -inf, or any NaN operand. The panic is the wrapper's invariant enforcement.

Solutions

  1. Check `is_finite()` on both operands before multiplying; skip or substitute 0/1 for degenerate cases.
  2. Replace the operator with fallible `NotNan::new(a.0 * b).ok()` and branch on the result.
  3. Clamp or guard upstream division/overflow that produces infinite operands before they reach this multiplication.

Example fix

// before
let scaled = notnan_weight * raw_factor; // 0 * inf panics
// after
let scaled = if raw_factor.is_finite() { notnan_weight * raw_factor } else { notnan_weight };
Defensive patterns

Strategy: validation

Validate before calling

fn can_mul(a: &NotNan<f64>, b: f64) -> bool {
    !(a.0 == 0.0 && b.is_infinite()) && !(a.0.is_infinite() && b == 0.0) && b.is_finite()
}

Type guard

fn is_finite_f64(x: f64) -> bool { x.is_finite() }

Try / catch

let scaled = std::panic::catch_unwind(|| notnan_w * raw_f).ok();

Prevention

When it happens

Trigger: Using `*` (std::ops::Mul<T> for NotNan<T>) where one operand underflows to zero and the other is infinite (0.0 * inf), or where the raw `other` argument is NaN.

Common situations: Scaling weights in numeric code where a coefficient became infinite due to earlier overflow (e.g. 1/0 normalization) while another factor is exactly 0; also multiplying by unvalidated parsed floats.

Related errors


AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/27273c1680cb2f6f. Report an issue: GitHub.

Appendix: source

Thrown at src/common/base/src/base/ordered_float.rs:1360

/// Panics if the provided value is NaN or the computation results in NaN
impl<T: FloatCore> Sub<T> for NotNan<T> {
    type Output = Self;

    #[inline]
    fn sub(self, other: T) -> Self {
        NotNan::new(self.0 - other).expect("Subtraction resulted in NaN")
    }
}

/// Multiplies a float directly.
///
/// Panics if the provided value is NaN or the computation results in NaN
impl<T: FloatCore> Mul<T> for NotNan<T> {
    type Output = Self;

    #[inline]
    fn mul(self, other: T) -> Self {
        NotNan::new(self.0 * other).expect("Multiplication resulted in NaN")
    }
}

impl<T: FloatCore + Product> Product for NotNan<T> {
    fn product<I: Iterator<Item = NotNan<T>>>(iter: I) -> Self {
        NotNan::new(iter.map(|v| v.0).product()).expect("Product resulted in NaN")
    }
}

impl<'a, T: FloatCore + Product + 'a> Product<&'a NotNan<T>> for NotNan<T> {
    #[inline]
    fn product<I: Iterator<Item = &'a NotNan<T>>>(iter: I) -> Self {
        iter.cloned().product()
    }
}

/// Divides a float directly.
///

View on GitHub (pinned to 288d84d76e)