{"record":{"id":"c7b9954e120ff8f5","repo":"databendlabs/databend","slug":"rem-resulted-in-nan","errorCode":null,"errorMessage":"Rem resulted in NaN","messagePattern":"Rem resulted in NaN","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/common/base/src/base/ordered_float.rs","lineNumber":1397,"sourceCode":"/// Panics if the provided value is NaN or the computation results in NaN\nimpl<T: FloatCore> Div<T> for NotNan<T> {\n    type Output = Self;\n\n    #[inline]\n    fn div(self, other: T) -> Self {\n        NotNan::new(self.0 / other).expect(\"Division resulted in NaN\")\n    }\n}\n\n/// Calculates `%` with a float directly.\n///\n/// Panics if the provided value is NaN or the computation results in NaN\nimpl<T: FloatCore> Rem<T> for NotNan<T> {\n    type Output = Self;\n\n    #[inline]\n    fn rem(self, other: T) -> Self {\n        NotNan::new(self.0 % other).expect(\"Rem resulted in NaN\")\n    }\n}\n\nmacro_rules! impl_not_nan_binop {\n    ($imp:ident, $method:ident, $assign_imp:ident, $assign_method:ident) => {\n        impl<T: FloatCore> $imp for NotNan<T> {\n            type Output = Self;\n\n            #[inline]\n            fn $method(self, other: Self) -> Self {\n                self.$method(other.0)\n            }\n        }\n\n        impl<T: FloatCore> $imp<&T> for NotNan<T> {\n            type Output = NotNan<T>;\n\n            #[inline]","sourceCodeStart":1379,"sourceCodeEnd":1415,"githubUrl":"https://github.com/databendlabs/databend/blob/288d84d76e20a2f8f7173bda9691eb6ece301aa9/src/common/base/src/base/ordered_float.rs#L1379-L1415","documentation":"The Rem impl for NotNan<T> computes self.0 % other and validates with NotNan::new, panicking with this message if the remainder is NaN. IEEE remainder is NaN for x % 0, inf % x, and NaN operands. This is the wrapper's fail-fast check keeping NaN out of ordered floats.","triggerScenarios":"Using `%` (std::ops::Rem<T> for NotNan<T>) with a divisor of 0.0, a dividend of ±inf, or a NaN `other` operand.","commonSituations":"Bucketing/binning values with a modulo computed from config that resolved to 0, or taking remainders of values that overflowed to infinity earlier in the pipeline.","solutions":["Validate before the op: `divisor.is_finite() && divisor != 0.0 && dividend.is_finite()`.","Fall back to `NotNan::new(a.0 % b).ok()` and handle None (e.g. skip the value or use a default bucket).","Ensure bucket-size config cannot be 0 and clamp upstream values so they never reach inf."],"exampleFix":"// before\nlet bucket = notnan_value % bucket_size; // panics if bucket_size == 0\n// after\nlet bucket = if bucket_size.is_finite() && bucket_size != 0.0 { notnan_value % bucket_size } else { notnan_value };","handlingStrategy":"validation","validationCode":"fn can_rem(a: &NotNan<f64>, b: f64) -> bool {\n    b != 0.0 && b.is_finite() && a.0.is_finite()\n}","typeGuard":"fn is_valid_modulus(x: f64) -> bool { x != 0.0 && x.is_finite() }","tryCatchPattern":"let bucket = std::panic::catch_unwind(|| notnan_value % bucket_size).ok();","preventionTips":["Validate bucket/modulus config values are nonzero and finite at load time.","Ensure the dividend cannot be inf (guard earlier arithmetic).","Use NotNan::new(a.0 % b).ok() for fallible remainder computation."],"tags":["rust","float","nan","modulo","remainder","panic"],"backgroundTag":"nan-arithmetic-panic","analyzedSha":"288d84d76e20a2f8f7173bda9691eb6ece301aa9","analyzedAt":"2026-09-11T11:29:36.208Z","contentChangedAt":"2026-09-11T11:29:36.208Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}