databendlabs/databend · error

Product resulted in NaN

Error message

Product resulted in NaN

What it means

The Product impl for NotNan<T> folds the iterator's floats with std's Product and rewraps with NotNan::new, panicking with this message if the product is NaN. A NaN product occurs when any factor is NaN or when a zero factor meets an infinite intermediate product (0 * inf).

Solutions

  1. Validate all factors with `is_finite()` before folding; filter or short-circuit degenerate products.
  2. Compute in f64 and wrap fallibly: `NotNan::new(vals.iter().product::<f64>()).ok()` with explicit NaN handling.
  3. Use logs/stable transforms for large products to avoid inf intermediates that later cancel against zeros.

Example fix

// before
let p: NotNan<f64> = factors.iter().copied().product(); // 0 * inf panics
// after
let p = if factors.iter().any(|v| !v.is_finite() || **v == 0.0) { None } else { Some(factors.iter().copied().product::<NotNan<f64>>()) };
Defensive patterns

Strategy: validation

Validate before calling

fn safe_product(vals: &[NotNan<f64>]) -> Option<NotNan<f64>> {
    let mut acc = 1.0f64;
    for v in vals {
        acc *= v.0;
        if acc.is_nan() { return None; }
    }
    NotNan::new(acc).ok()
}

Type guard

fn all_finite_nonzero_ok(vals: &[NotNan<f64>]) -> bool { vals.iter().all(|v| v.is_finite()) }

Try / catch

let p = std::panic::catch_unwind(|| vals.iter().copied().product::<NotNan<f64>>()).ok();

Prevention

When it happens

Trigger: `iter.product::<NotNan<f64>>()` where the running product overflows to inf and a later factor is 0.0, or where any factor is NaN (only reachable through unsafe NotNan construction or raw-value paths).

Common situations: Computing joint probabilities or compounded rates where underflow/overflow to 0/inf mixes with exact zeros; also product-reductions over sensor readings containing NaN sourced from upstream division by zero.

Related errors


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

Appendix: source

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

        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.
///
/// Panics if the provided value is NaN or the computation results in NaN
impl<T: FloatCore> Div<T> for NotNan<T> {
    type Output = Self;

    #[inline]
    fn div(self, other: T) -> Self {

View on GitHub (pinned to 288d84d76e)