{"record":{"id":"c67fda856aacd130","repo":"TheAlgorithms/Rust","slug":"encountered-nan","errorCode":null,"errorMessage":"Encountered NaN","messagePattern":"Encountered NaN","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/dynamic_programming/fractional_knapsack.rs","lineNumber":10,"sourceCode":"pub fn fractional_knapsack(mut capacity: f64, weights: Vec<f64>, values: Vec<f64>) -> f64 {\n    // vector of tuple of weights and their value/weight ratio\n    let mut weights: Vec<(f64, f64)> = weights\n        .iter()\n        .zip(values.iter())\n        .map(|(&w, &v)| (w, v / w))\n        .collect();\n\n    // sort in decreasing order by value/weight ratio\n    weights.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).expect(\"Encountered NaN\"));\n    dbg!(&weights);\n\n    // value to compute\n    let mut knapsack_value: f64 = 0.0;\n\n    // iterate through our vector.\n    for w in weights {\n        // w.0 is weight and w.1 value/weight ratio\n        if w.0 < capacity {\n            capacity -= w.0; // our sack is filling\n            knapsack_value += w.0 * w.1;\n            dbg!(&w.0, &knapsack_value);\n        } else {\n            // Multiply with capacity and not w.0\n            dbg!(&w.0, &knapsack_value);\n            knapsack_value += capacity * w.1;\n            break;\n        }","sourceCodeStart":1,"sourceCodeEnd":28,"githubUrl":"https://github.com/TheAlgorithms/Rust/blob/2c53ddfa4b43da4df34bc2f990c5e806f455cb90/src/dynamic_programming/fractional_knapsack.rs#L1-L28","documentation":"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.)","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet mut weights: Vec<(f64, f64)> = weights\n    .iter()\n    .zip(values.iter())\n    .map(|(&w, &v)| (w, v / w))\n    .collect();\nweights.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).expect(\"Encountered NaN\"));\n\n// after\nlet mut weights: Vec<(f64, f64)> = weights\n    .iter()\n    .zip(values.iter())\n    .map(|(&w, &v)| (w, v / w))\n    .filter(|&(w, r)| w.is_finite() && w > 0.0 && r.is_finite())\n    .collect();\nweights.sort_unstable_by(|a, b| b.1.total_cmp(&a.1));","handlingStrategy":"validation","validationCode":"fn valid_knapsack_input(capacity: f64, weights: &[f64], values: &[f64]) -> bool {\n    weights.len() == values.len()\n        && capacity.is_finite()\n        && capacity >= 0.0\n        && weights.iter().zip(values.iter()).all(|(&w, &v)| {\n            w.is_finite() && w > 0.0 && v.is_finite()\n        })\n}\n\nif valid_knapsack_input(capacity, &weights, &values) {\n    let total = fractional_knapsack(capacity, weights, values); // sort cannot see NaN\n}","typeGuard":null,"tryCatchPattern":"// fractional_knapsack panics rather than returning Err; validation is the\n// primary defense, but untrusted pipelines can fence the panic:\nlet total = match std::panic::catch_unwind(move || {\n    fractional_knapsack(capacity, weights, values)\n}) {\n    Ok(total) => total,\n    Err(_) => {\n        // NaN reached the sort: clean the data set and retry\n        0.0\n    }\n};","preventionTips":["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."],"tags":["rust","floating-point","nan","partial-cmp","sorting","greedy-algorithm"],"backgroundTag":"nan-in-float-comparison","analyzedSha":"2c53ddfa4b43da4df34bc2f990c5e806f455cb90","analyzedAt":"2026-08-16T21:59:20.899Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}