From cb41f133764b0d8d12a228af729bd77e113e0749 Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 9 Jul 2025 16:57:42 +0800 Subject: [PATCH 01/10] Enhance decimal casting functions to return errors on conversion failures --- arrow-cast/src/cast/decimal.rs | 4 ++-- arrow-cast/src/cast/mod.rs | 25 ++++++++++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/arrow-cast/src/cast/decimal.rs b/arrow-cast/src/cast/decimal.rs index 57dfc51d74c8..1e8d51673d6d 100644 --- a/arrow-cast/src/cast/decimal.rs +++ b/arrow-cast/src/cast/decimal.rs @@ -620,10 +620,10 @@ pub(crate) fn cast_decimal_to_float( op: F, ) -> Result where - F: Fn(D::Native) -> T::Native, + F: Fn(D::Native) -> Result, { let array = array.as_primitive::(); - let array = array.unary::<_, T>(op); + let array = array.try_unary::<_, T, _>(op)?; Ok(Arc::new(array)) } diff --git a/arrow-cast/src/cast/mod.rs b/arrow-cast/src/cast/mod.rs index 884a32197c99..0b85fc65e1b7 100644 --- a/arrow-cast/src/cast/mod.rs +++ b/arrow-cast/src/cast/mod.rs @@ -880,7 +880,7 @@ pub fn cast_with_options( scale, from_type, to_type, - |x: i128| x as f64, + |x: i128| Ok(x as f64), cast_options, ) } @@ -891,7 +891,11 @@ pub fn cast_with_options( scale, from_type, to_type, - |x: i256| x.to_f64().unwrap(), + |x: i256| { + x.to_f64().ok_or_else(|| { + ArrowError::CastError("Failed to convert Decimal256 to f64".to_string()) + }) + }, cast_options, ) } @@ -1964,7 +1968,7 @@ fn cast_from_decimal( where D: DecimalType + ArrowPrimitiveType, ::Native: ArrowNativeTypeOp + ToPrimitive, - F: Fn(D::Native) -> f64, + F: Fn(D::Native) -> Result, { use DataType::*; // cast decimal to other type @@ -1978,10 +1982,10 @@ where Int32 => cast_decimal_to_integer::(array, base, *scale, cast_options), Int64 => cast_decimal_to_integer::(array, base, *scale, cast_options), Float32 => cast_decimal_to_float::(array, |x| { - (as_float(x) / 10_f64.powi(*scale as i32)) as f32 + Ok((as_float(x)? / 10_f64.powi(*scale as i32)) as f32) }), Float64 => cast_decimal_to_float::(array, |x| { - as_float(x) / 10_f64.powi(*scale as i32) + as_float(x).map(|v| v / 10_f64.powi(*scale as i32)) }), Utf8View => value_to_string_view(array, cast_options), Utf8 => value_to_string::(array, cast_options), @@ -8660,6 +8664,17 @@ mod tests { "did not find expected error '{expected_error}' in actual error '{err}'" ); } + #[test] + fn test_cast_decimal256_to_f64_overflow() { + 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); + assert!(result.is_err()); + let err = result.unwrap_err().to_string(); + assert!(err.contains("Failed to convert Decimal256 to f64")); + } #[test] fn test_cast_decimal128_to_decimal128_negative_scale() { From 89d360e7abd4e3d30c304e7a210ed6823bd8a5f9 Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 9 Jul 2025 17:05:17 +0800 Subject: [PATCH 02/10] Enhance documentation for `cast_decimal_to_float` function to clarify conversion process and error handling --- arrow-cast/src/cast/decimal.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/arrow-cast/src/cast/decimal.rs b/arrow-cast/src/cast/decimal.rs index 1e8d51673d6d..9c32a72b14fa 100644 --- a/arrow-cast/src/cast/decimal.rs +++ b/arrow-cast/src/cast/decimal.rs @@ -614,7 +614,24 @@ where Ok(Arc::new(value_builder.finish())) } -// Cast the decimal array to floating-point array +/// Cast the decimal array to floating-point array. +/// +/// This function applies a conversion operation to each element in the decimal array +/// to produce a floating-point array. The operation function may fail with an +/// `ArrowError` for cases such as overflow, underflow, or other conversion errors. +/// +/// # Parameters +/// - `array`: The input decimal array to convert +/// - `op`: A conversion function that transforms each decimal value to a floating-point value. +/// This function returns `Result` to handle conversion failures. +/// +/// # Returns +/// Returns `Ok(ArrayRef)` on successful conversion of all elements, or `Err(ArrowError)` +/// if any element conversion fails. +/// +/// # Notes +/// Uses `try_unary` internally to handle the fallible conversion operation, ensuring +/// that any conversion errors are properly propagated rather than panicking. pub(crate) fn cast_decimal_to_float( array: &dyn Array, op: F, From cf9268d0fb781c412e2e5119c21d0e8fa6b8d2f5 Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 9 Jul 2025 17:13:04 +0800 Subject: [PATCH 03/10] Enhance error handling in `cast_decimal_to_float` function to include detailed context in error messages, such as failing element index and input value. --- arrow-cast/src/cast/decimal.rs | 38 ++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/arrow-cast/src/cast/decimal.rs b/arrow-cast/src/cast/decimal.rs index 9c32a72b14fa..389e8c6c320a 100644 --- a/arrow-cast/src/cast/decimal.rs +++ b/arrow-cast/src/cast/decimal.rs @@ -627,11 +627,17 @@ where /// /// # Returns /// Returns `Ok(ArrayRef)` on successful conversion of all elements, or `Err(ArrowError)` -/// if any element conversion fails. +/// if any element conversion fails. Error messages include the failing element's index +/// and input value for easier debugging. /// -/// # Notes -/// Uses `try_unary` internally to handle the fallible conversion operation, ensuring -/// that any conversion errors are properly propagated rather than panicking. +/// # Error Handling +/// When a conversion fails, the error message will include: +/// - The index of the failing element +/// - The original error from the conversion operation +/// - The input decimal value that caused the failure +/// +/// This provides fine-grained error context to help identify exactly which element +/// and value caused the casting operation to fail. pub(crate) fn cast_decimal_to_float( array: &dyn Array, op: F, @@ -640,8 +646,28 @@ where F: Fn(D::Native) -> Result, { let array = array.as_primitive::(); - let array = array.try_unary::<_, T, _>(op)?; - Ok(Arc::new(array)) + + // Use manual iteration to provide better error context with element indices + let mut builder = PrimitiveBuilder::::with_capacity(array.len()); + + for i in 0..array.len() { + if array.is_null(i) { + builder.append_null(); + } else { + let value = array.value(i); + match op(value) { + Ok(converted) => builder.append_value(converted), + Err(e) => { + return Err(ArrowError::CastError(format!( + "Failed to cast decimal to float at index {}: {} (input value: {:?})", + i, e, value + ))); + } + } + } + } + + Ok(Arc::new(builder.finish())) } #[cfg(test)] From 8ca4168337056dc55c5f1cf5105b55721f953cc1 Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 9 Jul 2025 17:13:39 +0800 Subject: [PATCH 04/10] Revert "Enhance error handling in `cast_decimal_to_float` function to include detailed context in error messages, such as failing element index and input value." This reverts commit cf9268d0fb781c412e2e5119c21d0e8fa6b8d2f5. --- arrow-cast/src/cast/decimal.rs | 38 ++++++---------------------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/arrow-cast/src/cast/decimal.rs b/arrow-cast/src/cast/decimal.rs index 389e8c6c320a..9c32a72b14fa 100644 --- a/arrow-cast/src/cast/decimal.rs +++ b/arrow-cast/src/cast/decimal.rs @@ -627,17 +627,11 @@ where /// /// # Returns /// Returns `Ok(ArrayRef)` on successful conversion of all elements, or `Err(ArrowError)` -/// if any element conversion fails. Error messages include the failing element's index -/// and input value for easier debugging. +/// if any element conversion fails. /// -/// # Error Handling -/// When a conversion fails, the error message will include: -/// - The index of the failing element -/// - The original error from the conversion operation -/// - The input decimal value that caused the failure -/// -/// This provides fine-grained error context to help identify exactly which element -/// and value caused the casting operation to fail. +/// # Notes +/// Uses `try_unary` internally to handle the fallible conversion operation, ensuring +/// that any conversion errors are properly propagated rather than panicking. pub(crate) fn cast_decimal_to_float( array: &dyn Array, op: F, @@ -646,28 +640,8 @@ where F: Fn(D::Native) -> Result, { let array = array.as_primitive::(); - - // Use manual iteration to provide better error context with element indices - let mut builder = PrimitiveBuilder::::with_capacity(array.len()); - - for i in 0..array.len() { - if array.is_null(i) { - builder.append_null(); - } else { - let value = array.value(i); - match op(value) { - Ok(converted) => builder.append_value(converted), - Err(e) => { - return Err(ArrowError::CastError(format!( - "Failed to cast decimal to float at index {}: {} (input value: {:?})", - i, e, value - ))); - } - } - } - } - - Ok(Arc::new(builder.finish())) + let array = array.try_unary::<_, T, _>(op)?; + Ok(Arc::new(array)) } #[cfg(test)] From 1bbfae3ee09facd1338002f37e66ddf9551cd0a9 Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 9 Jul 2025 17:18:16 +0800 Subject: [PATCH 05/10] made the code uniform by using the .map() pattern for both Float32 and Float64 conversions --- arrow-cast/src/cast/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arrow-cast/src/cast/mod.rs b/arrow-cast/src/cast/mod.rs index 0b85fc65e1b7..10a30fa034be 100644 --- a/arrow-cast/src/cast/mod.rs +++ b/arrow-cast/src/cast/mod.rs @@ -1982,7 +1982,7 @@ where Int32 => cast_decimal_to_integer::(array, base, *scale, cast_options), Int64 => cast_decimal_to_integer::(array, base, *scale, cast_options), Float32 => cast_decimal_to_float::(array, |x| { - Ok((as_float(x)? / 10_f64.powi(*scale as i32)) as f32) + as_float(x).map(|v| (v / 10_f64.powi(*scale as i32)) as f32) }), Float64 => cast_decimal_to_float::(array, |x| { as_float(x).map(|v| v / 10_f64.powi(*scale as i32)) From c6585017e08f10805353460d245148acd9294357 Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 9 Jul 2025 17:19:49 +0800 Subject: [PATCH 06/10] Add test for casting Decimal128 to Float64 with overflow handling --- arrow-cast/src/cast/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arrow-cast/src/cast/mod.rs b/arrow-cast/src/cast/mod.rs index 10a30fa034be..fcb56d05ac27 100644 --- a/arrow-cast/src/cast/mod.rs +++ b/arrow-cast/src/cast/mod.rs @@ -8676,6 +8676,21 @@ mod tests { assert!(err.contains("Failed to convert Decimal256 to f64")); } + #[test] + fn test_cast_decimal128_to_f64_overflow() { + // Test with a very large Decimal128 value that exceeds f64 range + // Using i128::MAX which is much larger than f64::MAX + let array = vec![Some(i128::MAX)]; + let array = create_decimal128_array(array, 38, 2).unwrap(); + let array = Arc::new(array) as ArrayRef; + + let result = cast(&array, &DataType::Float64); + assert!(result.is_err()); + let err = result.unwrap_err().to_string(); + assert!(err.contains("Failed to cast decimal to float at index")); + assert!(err.contains("input value")); + } + #[test] fn test_cast_decimal128_to_decimal128_negative_scale() { let input_type = DataType::Decimal128(20, 0); From 8e74ff2329a3a3c083cc26f6c67bc2f674800e5a Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 9 Jul 2025 17:20:46 +0800 Subject: [PATCH 07/10] Add tests for overflow handling when casting Decimal128 and Decimal256 to Float32 --- arrow-cast/src/cast/mod.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/arrow-cast/src/cast/mod.rs b/arrow-cast/src/cast/mod.rs index fcb56d05ac27..0905021739f4 100644 --- a/arrow-cast/src/cast/mod.rs +++ b/arrow-cast/src/cast/mod.rs @@ -8691,6 +8691,36 @@ mod tests { assert!(err.contains("input value")); } + #[test] + fn test_cast_decimal128_to_f32_overflow() { + // Test with a Decimal128 value that exceeds f32 range + // Using a large value that's within f64 range but exceeds f32::MAX + let large_value = 1_i128 << 100; // Much larger than f32::MAX (≈ 3.4 × 10^38) + let array = vec![Some(large_value)]; + let array = create_decimal128_array(array, 38, 2).unwrap(); + let array = Arc::new(array) as ArrayRef; + + let result = cast(&array, &DataType::Float32); + assert!(result.is_err()); + let err = result.unwrap_err().to_string(); + assert!(err.contains("Failed to cast decimal to float at index")); + assert!(err.contains("input value")); + } + + #[test] + fn test_cast_decimal256_to_f32_overflow() { + // Test with a Decimal256 value that exceeds f32 range + 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::Float32); + assert!(result.is_err()); + let err = result.unwrap_err().to_string(); + assert!(err.contains("Failed to cast decimal to float at index")); + assert!(err.contains("input value")); + } + #[test] fn test_cast_decimal128_to_decimal128_negative_scale() { let input_type = DataType::Decimal128(20, 0); From 321781343c5ae23da47b6f65dee817dc76792ef6 Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 9 Jul 2025 19:26:52 +0800 Subject: [PATCH 08/10] Revert "Enhance decimal casting functions to return errors on conversion failures" This reverts commit f25ec6b3ba8561f8a66b276d9d7869f8636ce48c. --- arrow-cast/src/cast/decimal.rs | 23 ++--------- arrow-cast/src/cast/mod.rs | 70 +++------------------------------- 2 files changed, 8 insertions(+), 85 deletions(-) diff --git a/arrow-cast/src/cast/decimal.rs b/arrow-cast/src/cast/decimal.rs index 9c32a72b14fa..57dfc51d74c8 100644 --- a/arrow-cast/src/cast/decimal.rs +++ b/arrow-cast/src/cast/decimal.rs @@ -614,33 +614,16 @@ where Ok(Arc::new(value_builder.finish())) } -/// Cast the decimal array to floating-point array. -/// -/// This function applies a conversion operation to each element in the decimal array -/// to produce a floating-point array. The operation function may fail with an -/// `ArrowError` for cases such as overflow, underflow, or other conversion errors. -/// -/// # Parameters -/// - `array`: The input decimal array to convert -/// - `op`: A conversion function that transforms each decimal value to a floating-point value. -/// This function returns `Result` to handle conversion failures. -/// -/// # Returns -/// Returns `Ok(ArrayRef)` on successful conversion of all elements, or `Err(ArrowError)` -/// if any element conversion fails. -/// -/// # Notes -/// Uses `try_unary` internally to handle the fallible conversion operation, ensuring -/// that any conversion errors are properly propagated rather than panicking. +// Cast the decimal array to floating-point array pub(crate) fn cast_decimal_to_float( array: &dyn Array, op: F, ) -> Result where - F: Fn(D::Native) -> Result, + F: Fn(D::Native) -> T::Native, { let array = array.as_primitive::(); - let array = array.try_unary::<_, T, _>(op)?; + let array = array.unary::<_, T>(op); Ok(Arc::new(array)) } diff --git a/arrow-cast/src/cast/mod.rs b/arrow-cast/src/cast/mod.rs index 0905021739f4..884a32197c99 100644 --- a/arrow-cast/src/cast/mod.rs +++ b/arrow-cast/src/cast/mod.rs @@ -880,7 +880,7 @@ pub fn cast_with_options( scale, from_type, to_type, - |x: i128| Ok(x as f64), + |x: i128| x as f64, cast_options, ) } @@ -891,11 +891,7 @@ pub fn cast_with_options( scale, from_type, to_type, - |x: i256| { - x.to_f64().ok_or_else(|| { - ArrowError::CastError("Failed to convert Decimal256 to f64".to_string()) - }) - }, + |x: i256| x.to_f64().unwrap(), cast_options, ) } @@ -1968,7 +1964,7 @@ fn cast_from_decimal( where D: DecimalType + ArrowPrimitiveType, ::Native: ArrowNativeTypeOp + ToPrimitive, - F: Fn(D::Native) -> Result, + F: Fn(D::Native) -> f64, { use DataType::*; // cast decimal to other type @@ -1982,10 +1978,10 @@ where Int32 => cast_decimal_to_integer::(array, base, *scale, cast_options), Int64 => cast_decimal_to_integer::(array, base, *scale, cast_options), Float32 => cast_decimal_to_float::(array, |x| { - as_float(x).map(|v| (v / 10_f64.powi(*scale as i32)) as f32) + (as_float(x) / 10_f64.powi(*scale as i32)) as f32 }), Float64 => cast_decimal_to_float::(array, |x| { - as_float(x).map(|v| v / 10_f64.powi(*scale as i32)) + as_float(x) / 10_f64.powi(*scale as i32) }), Utf8View => value_to_string_view(array, cast_options), Utf8 => value_to_string::(array, cast_options), @@ -8664,62 +8660,6 @@ mod tests { "did not find expected error '{expected_error}' in actual error '{err}'" ); } - #[test] - fn test_cast_decimal256_to_f64_overflow() { - 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); - assert!(result.is_err()); - let err = result.unwrap_err().to_string(); - assert!(err.contains("Failed to convert Decimal256 to f64")); - } - - #[test] - fn test_cast_decimal128_to_f64_overflow() { - // Test with a very large Decimal128 value that exceeds f64 range - // Using i128::MAX which is much larger than f64::MAX - let array = vec![Some(i128::MAX)]; - let array = create_decimal128_array(array, 38, 2).unwrap(); - let array = Arc::new(array) as ArrayRef; - - let result = cast(&array, &DataType::Float64); - assert!(result.is_err()); - let err = result.unwrap_err().to_string(); - assert!(err.contains("Failed to cast decimal to float at index")); - assert!(err.contains("input value")); - } - - #[test] - fn test_cast_decimal128_to_f32_overflow() { - // Test with a Decimal128 value that exceeds f32 range - // Using a large value that's within f64 range but exceeds f32::MAX - let large_value = 1_i128 << 100; // Much larger than f32::MAX (≈ 3.4 × 10^38) - let array = vec![Some(large_value)]; - let array = create_decimal128_array(array, 38, 2).unwrap(); - let array = Arc::new(array) as ArrayRef; - - let result = cast(&array, &DataType::Float32); - assert!(result.is_err()); - let err = result.unwrap_err().to_string(); - assert!(err.contains("Failed to cast decimal to float at index")); - assert!(err.contains("input value")); - } - - #[test] - fn test_cast_decimal256_to_f32_overflow() { - // Test with a Decimal256 value that exceeds f32 range - 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::Float32); - assert!(result.is_err()); - let err = result.unwrap_err().to_string(); - assert!(err.contains("Failed to cast decimal to float at index")); - assert!(err.contains("input value")); - } #[test] fn test_cast_decimal128_to_decimal128_negative_scale() { From ea83122a9241f1835c8d7b91aa8738290bfe7d2e Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Wed, 9 Jul 2025 21:51:42 +0800 Subject: [PATCH 09/10] fix(decimal cast): convert decimal to float with saturation instead of error on overflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Changed decimal-to-float casts to use lossy conversion consistent with IEEE semantics, saturating to ±INFINITY instead of returning an error on overflow or out-of-range values. - Updated `cast_decimal_to_float` to use infallible conversion function signature. - Added `decimal256_to_f64` helper for Decimal256 to f64 conversion with saturation. - Adjusted casting logic in `cast_with_options` accordingly. - Removed tests that expected errors on decimal-to-float overflow since now conversion saturates. - Clarified documentation to specify that decimal to float casts are lossy and saturate on overflow. --- arrow-cast/src/cast/decimal.rs | 6 +++++- arrow-cast/src/cast/mod.rs | 25 ++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/arrow-cast/src/cast/decimal.rs b/arrow-cast/src/cast/decimal.rs index 57dfc51d74c8..597f384fa452 100644 --- a/arrow-cast/src/cast/decimal.rs +++ b/arrow-cast/src/cast/decimal.rs @@ -614,7 +614,11 @@ where Ok(Arc::new(value_builder.finish())) } -// Cast the decimal array to floating-point array +/// Cast a decimal array to a floating point array. +/// +/// Conversion is lossy and follows standard floating point semantics. Values +/// that exceed the representable range become `INFINITY` or `-INFINITY` without +/// returning an error. pub(crate) fn cast_decimal_to_float( array: &dyn Array, op: F, diff --git a/arrow-cast/src/cast/mod.rs b/arrow-cast/src/cast/mod.rs index 884a32197c99..72330ad42c3d 100644 --- a/arrow-cast/src/cast/mod.rs +++ b/arrow-cast/src/cast/mod.rs @@ -603,6 +603,8 @@ fn timestamp_to_date32( /// * Temporal to/from backing Primitive: zero-copy with data type change /// * `Float32/Float64` to `Decimal(precision, scale)` rounds to the `scale` decimals /// (i.e. casting `6.4999` to `Decimal(10, 1)` becomes `6.5`). +/// * `Decimal` to `Float32/Float64` is lossy and values outside the representable +/// range become `INFINITY` or `-INFINITY` without error. /// /// Unsupported Casts (check with `can_cast_types` before calling): /// * To or from `StructArray` @@ -891,7 +893,7 @@ pub fn cast_with_options( scale, from_type, to_type, - |x: i256| x.to_f64().unwrap(), + |x: i256| decimal256_to_f64(x), cast_options, ) } @@ -1993,6 +1995,17 @@ 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, @@ -8660,6 +8673,16 @@ mod tests { "did not find expected error '{expected_error}' in actual error '{err}'" ); } + #[test] + fn test_cast_decimal256_to_f64_overflow() { + 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()); + } #[test] fn test_cast_decimal128_to_decimal128_negative_scale() { From 05ace0c53dea226d38ab6cd5eb8b923a2725f65a Mon Sep 17 00:00:00 2001 From: Siew Kam Onn Date: Thu, 10 Jul 2025 13:38:14 +0800 Subject: [PATCH 10/10] test(decimal cast): add tests for positive and negative overflow when casting Decimal256 to Float64 --- arrow-cast/src/cast/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arrow-cast/src/cast/mod.rs b/arrow-cast/src/cast/mod.rs index 72330ad42c3d..aad20e86fbe7 100644 --- a/arrow-cast/src/cast/mod.rs +++ b/arrow-cast/src/cast/mod.rs @@ -8675,6 +8675,7 @@ mod tests { } #[test] fn test_cast_decimal256_to_f64_overflow() { + // Test positive overflow (positive infinity) let array = vec![Some(i256::MAX)]; let array = create_decimal256_array(array, 76, 2).unwrap(); let array = Arc::new(array) as ArrayRef; @@ -8682,6 +8683,17 @@ mod tests { 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 + + // Test negative overflow (negative infinity) + 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 } #[test]