This is an automated email from the ASF dual-hosted git repository.

viirya pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/master by this push:
     new 06e1111c2 Support casting from decimal256 to float (#3267)
06e1111c2 is described below

commit 06e1111c21fb56f61b405aafa967f2c6fd321a18
Author: Liang-Chi Hsieh <[email protected]>
AuthorDate: Mon Dec 5 00:43:36 2022 -0800

    Support casting from decimal256 to float (#3267)
---
 arrow-cast/src/cast.rs | 154 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 128 insertions(+), 26 deletions(-)

diff --git a/arrow-cast/src/cast.rs b/arrow-cast/src/cast.rs
index 6d43c996c..272a422eb 100644
--- a/arrow-cast/src/cast.rs
+++ b/arrow-cast/src/cast.rs
@@ -141,7 +141,7 @@ pub fn can_cast_types(from_type: &DataType, to_type: 
&DataType) -> bool {
         (Decimal256(_, _), UInt8 | UInt16 | UInt32 | UInt64) |
         // decimal to signed numeric
         (Decimal128(_, _), Null | Int8 | Int16 | Int32 | Int64 | Float32 | 
Float64) |
-        (Decimal256(_, _), Null | Int8 | Int16 | Int32 | Int64) => true,
+        (Decimal256(_, _), Null | Int8 | Int16 | Int32 | Int64 | Float32 | 
Float64) => true,
         (Decimal128(_, _), _) => false,
         (_, Decimal128(_, _)) => false,
         (Decimal256(_, _), _) => false,
@@ -496,23 +496,16 @@ where
 }
 
 // cast the decimal array to floating-point array
-macro_rules! cast_decimal_to_float {
-    ($ARRAY:expr, $SCALE : ident, $VALUE_BUILDER: ident, $NATIVE_TYPE : ty) => 
{{
-        let array = $ARRAY.as_any().downcast_ref::<Decimal128Array>().unwrap();
-        let div = 10_f64.powi(*$SCALE as i32);
-        let mut value_builder = $VALUE_BUILDER::with_capacity(array.len());
-        for i in 0..array.len() {
-            if array.is_null(i) {
-                value_builder.append_null();
-            } else {
-                // The range of f32 or f64 is larger than i128, we don't need 
to check overflow.
-                // cast the i128 to f64 will lose precision, for example the 
`112345678901234568` will be as `112345678901234560`.
-                let v = (array.value(i) as f64 / div) as $NATIVE_TYPE;
-                value_builder.append_value(v);
-            }
-        }
-        Ok(Arc::new(value_builder.finish()))
-    }};
+fn cast_decimal_to_float<D: DecimalType, T: ArrowPrimitiveType, F>(
+    array: &ArrayRef,
+    op: F,
+) -> Result<ArrayRef, ArrowError>
+where
+    F: Fn(D::Native) -> T::Native,
+{
+    let array = array.as_any().downcast_ref::<PrimitiveArray<D>>().unwrap();
+    let array = array.unary::<_, T>(op);
+    Ok(Arc::new(array))
 }
 
 // cast the List array to Utf8 array
@@ -796,10 +789,14 @@ pub fn cast_with_options(
                     cast_options,
                 ),
                 Float32 => {
-                    cast_decimal_to_float!(array, scale, Float32Builder, f32)
+                    cast_decimal_to_float::<Decimal128Type, Float32Type, 
_>(array, |x| {
+                        (x as f64 / 10_f64.powi(*scale as i32)) as f32
+                    })
                 }
                 Float64 => {
-                    cast_decimal_to_float!(array, scale, Float64Builder, f64)
+                    cast_decimal_to_float::<Decimal128Type, Float64Type, 
_>(array, |x| {
+                        (x as f64 / 10_f64.powi(*scale as i32)) as f64
+                    })
                 }
                 Null => Ok(new_null_array(to_type, array.len())),
                 _ => Err(ArrowError::CastError(format!(
@@ -859,6 +856,16 @@ pub fn cast_with_options(
                     *scale,
                     cast_options,
                 ),
+                Float32 => {
+                    cast_decimal_to_float::<Decimal256Type, Float32Type, 
_>(array, |x| {
+                        (x.to_f64().unwrap() / 10_f64.powi(*scale as i32)) as 
f32
+                    })
+                }
+                Float64 => {
+                    cast_decimal_to_float::<Decimal256Type, Float64Type, 
_>(array, |x| {
+                        (x.to_f64().unwrap() / 10_f64.powi(*scale as i32)) as 
f64
+                    })
+                }
                 Null => Ok(new_null_array(to_type, array.len())),
                 _ => Err(ArrowError::CastError(format!(
                     "Casting from {:?} to {:?} not supported",
@@ -3735,16 +3742,28 @@ mod tests {
         // f32
         generate_cast_test_case!(
             &array,
-            Int64Array,
-            &DataType::Int64,
-            vec![Some(1_i64), Some(2_i64), Some(3_i64), None, Some(5_i64)]
+            Float32Array,
+            &DataType::Float32,
+            vec![
+                Some(1.25_f32),
+                Some(2.25_f32),
+                Some(3.25_f32),
+                None,
+                Some(5.25_f32)
+            ]
         );
         // f64
         generate_cast_test_case!(
             &array,
-            Int64Array,
-            &DataType::Int64,
-            vec![Some(1_i64), Some(2_i64), Some(3_i64), None, Some(5_i64)]
+            Float64Array,
+            &DataType::Float64,
+            vec![
+                Some(1.25_f64),
+                Some(2.25_f64),
+                Some(3.25_f64),
+                None,
+                Some(5.25_f64)
+            ]
         );
 
         // overflow test: out of range of max u8
@@ -3904,6 +3923,32 @@ mod tests {
             &DataType::Int64,
             vec![Some(1_i64), Some(2_i64), Some(3_i64), None, Some(5_i64)]
         );
+        // f32
+        generate_cast_test_case!(
+            &array,
+            Float32Array,
+            &DataType::Float32,
+            vec![
+                Some(1.25_f32),
+                Some(2.25_f32),
+                Some(3.25_f32),
+                None,
+                Some(5.25_f32)
+            ]
+        );
+        // f64
+        generate_cast_test_case!(
+            &array,
+            Float64Array,
+            &DataType::Float64,
+            vec![
+                Some(1.25_f64),
+                Some(2.25_f64),
+                Some(3.25_f64),
+                None,
+                Some(5.25_f64)
+            ]
+        );
 
         // overflow test: out of range of max i8
         let value_array: Vec<Option<i256>> = 
vec![Some(i256::from_i128(24400))];
@@ -3920,6 +3965,63 @@ mod tests {
             cast_with_options(&array, &DataType::Int8, &CastOptions { safe: 
true });
         assert!(casted_array.is_ok());
         assert!(casted_array.unwrap().is_null(0));
+
+        // loss the precision: convert decimal to f32、f64
+        // f32
+        // 112345678_f32 and 112345679_f32 are same, so the 112345679_f32 will 
lose precision.
+        let value_array: Vec<Option<i256>> = vec![
+            Some(i256::from_i128(125)),
+            Some(i256::from_i128(225)),
+            Some(i256::from_i128(325)),
+            None,
+            Some(i256::from_i128(525)),
+            Some(i256::from_i128(112345678)),
+            Some(i256::from_i128(112345679)),
+        ];
+        let decimal_array = create_decimal256_array(value_array, 76, 
2).unwrap();
+        let array = Arc::new(decimal_array) as ArrayRef;
+        generate_cast_test_case!(
+            &array,
+            Float32Array,
+            &DataType::Float32,
+            vec![
+                Some(1.25_f32),
+                Some(2.25_f32),
+                Some(3.25_f32),
+                None,
+                Some(5.25_f32),
+                Some(1_123_456.7_f32),
+                Some(1_123_456.7_f32)
+            ]
+        );
+
+        // f64
+        // 112345678901234568_f64 and 112345678901234560_f64 are same, so the 
112345678901234568_f64 will lose precision.
+        let value_array: Vec<Option<i256>> = vec![
+            Some(i256::from_i128(125)),
+            Some(i256::from_i128(225)),
+            Some(i256::from_i128(325)),
+            None,
+            Some(i256::from_i128(525)),
+            Some(i256::from_i128(112345678901234568)),
+            Some(i256::from_i128(112345678901234560)),
+        ];
+        let decimal_array = create_decimal256_array(value_array, 76, 
2).unwrap();
+        let array = Arc::new(decimal_array) as ArrayRef;
+        generate_cast_test_case!(
+            &array,
+            Float64Array,
+            &DataType::Float64,
+            vec![
+                Some(1.25_f64),
+                Some(2.25_f64),
+                Some(3.25_f64),
+                None,
+                Some(5.25_f64),
+                Some(1_123_456_789_012_345.6_f64),
+                Some(1_123_456_789_012_345.6_f64),
+            ]
+        );
     }
 
     #[test]

Reply via email to