diff --git a/arrow-buffer/src/bigint/mod.rs b/arrow-buffer/src/bigint/mod.rs index 9868ab55cc11..92f11d68d318 100644 --- a/arrow-buffer/src/bigint/mod.rs +++ b/arrow-buffer/src/bigint/mod.rs @@ -821,6 +821,20 @@ impl ToPrimitive for i256 { } } + fn to_f64(&self) -> Option { + let mag = if let Some(u) = self.checked_abs() { + let (low, high) = u.to_parts(); + (high as f64) * 2_f64.powi(128) + (low as f64) + } else { + // self == MIN + 2_f64.powi(255) + }; + if *self < i256::ZERO { + Some(-mag) + } else { + Some(mag) + } + } fn to_u64(&self) -> Option { let as_i128 = self.low as i128; @@ -1264,4 +1278,29 @@ mod tests { } } } + + #[test] + fn test_decimal256_to_f64_typical_values() { + let v = i256::from_i128(42_i128); + assert_eq!(v.to_f64().unwrap(), 42.0); + + let v = i256::from_i128(-123456789012345678i128); + assert_eq!(v.to_f64().unwrap(), -123456789012345678.0); + } + + #[test] + fn test_decimal256_to_f64_large_positive_value() { + let max_f = f64::MAX; + let big = i256::from_f64(max_f * 2.0).unwrap_or(i256::MAX); + let out = big.to_f64().unwrap(); + assert!(out.is_finite() && out.is_sign_positive()); + } + + #[test] + fn test_decimal256_to_f64_large_negative_value() { + let max_f = f64::MAX; + let big_neg = i256::from_f64(-(max_f * 2.0)).unwrap_or(i256::MIN); + let out = big_neg.to_f64().unwrap(); + assert!(out.is_finite() && out.is_sign_negative()); + } } diff --git a/arrow-cast/src/cast/mod.rs b/arrow-cast/src/cast/mod.rs index dbe4401c7863..0d4af3f7e0cb 100644 --- a/arrow-cast/src/cast/mod.rs +++ b/arrow-cast/src/cast/mod.rs @@ -893,7 +893,7 @@ pub fn cast_with_options( scale, from_type, to_type, - |x: i256| decimal256_to_f64(x), + |x: i256| x.to_f64().expect("All i256 values fit in f64"), cast_options, ) } @@ -1995,17 +1995,6 @@ where } } -/// Convert a [`i256`] to `f64` saturating to infinity on overflow. -fn decimal256_to_f64(v: i256) -> f64 { - v.to_f64().unwrap_or_else(|| { - if v.is_negative() { - f64::NEG_INFINITY - } else { - f64::INFINITY - } - }) -} - fn cast_to_decimal( array: &dyn Array, base: M, @@ -2439,6 +2428,7 @@ where #[cfg(test)] mod tests { use super::*; + use arrow_buffer::i256; use arrow_buffer::{Buffer, IntervalDayTime, NullBuffer}; use chrono::NaiveDate; use half::f16; @@ -8674,26 +8664,26 @@ mod tests { ); } #[test] - fn test_cast_decimal256_to_f64_overflow() { - // Test positive overflow (positive infinity) + fn test_cast_decimal256_to_f64_no_overflow() { + // Test casting i256::MAX: should produce a large finite positive value let array = vec![Some(i256::MAX)]; let array = create_decimal256_array(array, 76, 2).unwrap(); let array = Arc::new(array) as ArrayRef; let result = cast(&array, &DataType::Float64).unwrap(); let result = result.as_primitive::(); - assert!(result.value(0).is_infinite()); - assert!(result.value(0) > 0.0); // Positive infinity + assert!(result.value(0).is_finite()); + assert!(result.value(0) > 0.0); // Positive result - // Test negative overflow (negative infinity) + // Test casting i256::MIN: should produce a large finite negative value let array = vec![Some(i256::MIN)]; let array = create_decimal256_array(array, 76, 2).unwrap(); let array = Arc::new(array) as ArrayRef; let result = cast(&array, &DataType::Float64).unwrap(); let result = result.as_primitive::(); - assert!(result.value(0).is_infinite()); - assert!(result.value(0) < 0.0); // Negative infinity + assert!(result.value(0).is_finite()); + assert!(result.value(0) < 0.0); // Negative result } #[test] @@ -8724,6 +8714,15 @@ mod tests { assert_eq!("3123460", decimal_arr.value_as_string(2)); } + #[test] + fn decimal128_min_max_to_f64() { + // Ensure Decimal128 i128::MIN/MAX round-trip cast + let min128 = i128::MIN; + let max128 = i128::MAX; + assert_eq!(min128 as f64, min128 as f64); + assert_eq!(max128 as f64, max128 as f64); + } + #[test] fn test_cast_numeric_to_decimal128_negative() { let decimal_type = DataType::Decimal128(38, -1);