TheAlgorithms/Rust · error
Encountered NaN
Error message
Encountered NaN
What it means
fractional_knapsack panics with this .expect() message while sorting items by value/weight ratio (src/dynamic_programming/fractional_knapsack.rs:10). Ratios are computed as v / w, so a weight of 0.0 (with value 0.0) produces NaN, and NaN can also arrive directly in the weights or values vectors; f64::partial_cmp returns None whenever either operand is NaN, and the code expects a Some, aborting the thread. This is the classic float-ordering pitfall: NaN violates total order, so every partial_cmp-based sort must handle None. (Infinity alone does NOT trigger this — inf is orderable; only NaN does.)
Source
Thrown at src/dynamic_programming/fractional_knapsack.rs:10
pub fn fractional_knapsack(mut capacity: f64, weights: Vec<f64>, values: Vec<f64>) -> f64 {
// vector of tuple of weights and their value/weight ratio
let mut weights: Vec<(f64, f64)> = weights
.iter()
.zip(values.iter())
.map(|(&w, &v)| (w, v / w))
.collect();
// sort in decreasing order by value/weight ratio
weights.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).expect("Encountered NaN"));
dbg!(&weights);
// value to compute
let mut knapsack_value: f64 = 0.0;
// iterate through our vector.
for w in weights {
// w.0 is weight and w.1 value/weight ratio
if w.0 < capacity {
capacity -= w.0; // our sack is filling
knapsack_value += w.0 * w.1;
dbg!(&w.0, &knapsack_value);
} else {
// Multiply with capacity and not w.0
dbg!(&w.0, &knapsack_value);
knapsack_value += capacity * w.1;
break;
}View on GitHub (pinned to 2c53ddfa4b)
Solutions
- Validate inputs before calling: every weight must be finite and > 0.0, every value finite, and weights.len() == values.len(); handle zero-weight items separately (they are taken for free).
- If you control the code, sort with the NaN-tolerant total order: weights.sort_unstable_by(|a, b| b.1.total_cmp(&a.1)) — removes the panic entirely and is the idiomatic float sort.
- Or filter non-finite ratios before sorting: weights.retain(|&(w, r)| w.is_finite() && r.is_finite());
- If NaN inputs are expected in your domain, decide the policy explicitly (treat as lowest priority, or return an error) instead of relying on the panic.
- If you fork the file, also delete the leftover dbg!() calls — they print the whole item list to stderr on every run.
Example fix
// before
let mut weights: Vec<(f64, f64)> = weights
.iter()
.zip(values.iter())
.map(|(&w, &v)| (w, v / w))
.collect();
weights.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).expect("Encountered NaN"));
// after
let mut weights: Vec<(f64, f64)> = weights
.iter()
.zip(values.iter())
.map(|(&w, &v)| (w, v / w))
.filter(|&(w, r)| w.is_finite() && w > 0.0 && r.is_finite())
.collect();
weights.sort_unstable_by(|a, b| b.1.total_cmp(&a.1)); Defensive patterns
Strategy: validation
Validate before calling
fn valid_knapsack_input(capacity: f64, weights: &[f64], values: &[f64]) -> bool {
weights.len() == values.len()
&& capacity.is_finite()
&& capacity >= 0.0
&& weights.iter().zip(values.iter()).all(|(&w, &v)| {
w.is_finite() && w > 0.0 && v.is_finite()
})
}
if valid_knapsack_input(capacity, &weights, &values) {
let total = fractional_knapsack(capacity, weights, values); // sort cannot see NaN
} Try / catch
// fractional_knapsack panics rather than returning Err; validation is the
// primary defense, but untrusted pipelines can fence the panic:
let total = match std::panic::catch_unwind(move || {
fractional_knapsack(capacity, weights, values)
}) {
Ok(total) => total,
Err(_) => {
// NaN reached the sort: clean the data set and retry
0.0
}
}; Prevention
- Check weights.len() == values.len() first — zip() silently truncates mismatched inputs and returns a wrong (not panicked) answer.
- Assert every weight is finite and > 0.0 and every value is finite before calling.
- Parse numbers with explicit missing-field handling instead of defaults that become 0.0 weights.
- Prefer f64::total_cmp over partial_cmp().unwrap()/expect() in any float sort you write.
- Treat zero-weight items specially: they add value at zero capacity cost.
When it happens
Trigger: A weight of 0.0 with value 0.0 (0/0 → NaN ratio); passing f64::NAN anywhere in values — the crate's own test_nan does exactly this; a NaN weight (v / NaN → NaN); NaNs propagated from upstream math such as missing CSV/JSON fields parsed as 0.0 or NaN, or earlier 0.0/0.0 computations.
Common situations: Datasets with missing numeric fields defaulted to 0.0 (zero-weight items) fed straight into the function; inventory problems where items legitimately have zero weight; reusing floats from log/sqrt of negative inputs that already became NaN; assuming sort_unstable_by with partial_cmp is a total order and unwrap/expect-ing it in code review.
Related errors
AI-assisted analysis of TheAlgorithms/Rust@2c53ddfa4b (2026-08-16).
Data as JSON: /api/errors/c67fda856aacd130.
Report an issue: GitHub.