You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@arrow.apache.org by tu...@apache.org on 2022/12/04 13:58:19 UTC

[arrow-rs] branch master updated: Simplify decimal cast logic (#3264)

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

tustvold 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 9f64476d8 Simplify decimal cast logic (#3264)
9f64476d8 is described below

commit 9f64476d837e834b2616a8b6cd19bbd860defd39
Author: Raphael Taylor-Davies <17...@users.noreply.github.com>
AuthorDate: Sun Dec 4 13:58:13 2022 +0000

    Simplify decimal cast logic (#3264)
    
    * Simplify decimal cast logic
    
    * Add test
---
 arrow-cast/src/cast.rs | 668 +++++++++++++------------------------------------
 1 file changed, 169 insertions(+), 499 deletions(-)

diff --git a/arrow-cast/src/cast.rs b/arrow-cast/src/cast.rs
index cddbf0d95..372f0bd3c 100644
--- a/arrow-cast/src/cast.rs
+++ b/arrow-cast/src/cast.rs
@@ -330,7 +330,7 @@ where
     <T as ArrowPrimitiveType>::Native: AsPrimitive<M>,
     M: ArrowNativeTypeOp,
 {
-    let mul_or_div: M = base.pow_checked(scale.unsigned_abs() as u32).map_err(|_| {
+    let scale_factor = base.pow_checked(scale.unsigned_abs() as u32).map_err(|_| {
         ArrowError::CastError(format!(
             "Cannot cast to {:?}({}, {}). The scale causes overflow.",
             D::PREFIX,
@@ -339,29 +339,19 @@ where
         ))
     })?;
 
-    if scale < 0 {
-        if cast_options.safe {
-            array
-                .unary_opt::<_, D>(|v| v.as_().div_checked(mul_or_div).ok())
-                .with_precision_and_scale(precision, scale)
-                .map(|a| Arc::new(a) as ArrayRef)
-        } else {
-            array
-                .try_unary::<_, D, _>(|v| v.as_().div_checked(mul_or_div))
-                .and_then(|a| a.with_precision_and_scale(precision, scale))
-                .map(|a| Arc::new(a) as ArrayRef)
+    let array = if scale < 0 {
+        match cast_options.safe {
+            true => array.unary_opt::<_, D>(|v| v.as_().div_checked(scale_factor).ok()),
+            false => array.try_unary::<_, D, _>(|v| v.as_().div_checked(scale_factor))?,
         }
-    } else if cast_options.safe {
-        array
-            .unary_opt::<_, D>(|v| v.as_().mul_checked(mul_or_div).ok())
-            .with_precision_and_scale(precision, scale)
-            .map(|a| Arc::new(a) as ArrayRef)
     } else {
-        array
-            .try_unary::<_, D, _>(|v| v.as_().mul_checked(mul_or_div))
-            .and_then(|a| a.with_precision_and_scale(precision, scale))
-            .map(|a| Arc::new(a) as ArrayRef)
-    }
+        match cast_options.safe {
+            true => array.unary_opt::<_, D>(|v| v.as_().mul_checked(scale_factor).ok()),
+            false => array.try_unary::<_, D, _>(|v| v.as_().mul_checked(scale_factor))?,
+        }
+    };
+
+    Ok(Arc::new(array.with_precision_and_scale(precision, scale)?))
 }
 
 fn cast_floating_point_to_decimal128<T: ArrowPrimitiveType>(
@@ -383,22 +373,17 @@ where
     } else {
         array
             .try_unary::<_, Decimal128Type, _>(|v| {
-                mul.mul_checked(v.as_()).and_then(|value| {
-                    let mul_v = value.round();
-                    let integer: i128 = mul_v.to_i128().ok_or_else(|| {
-                        ArrowError::CastError(format!(
-                            "Cannot cast to {}({}, {}). Overflowing on {:?}",
-                            Decimal128Type::PREFIX,
-                            precision,
-                            scale,
-                            v
-                        ))
-                    })?;
-
-                    Ok(integer)
+                (mul * v.as_()).round().to_i128().ok_or_else(|| {
+                    ArrowError::CastError(format!(
+                        "Cannot cast to {}({}, {}). Overflowing on {:?}",
+                        Decimal128Type::PREFIX,
+                        precision,
+                        scale,
+                        v
+                    ))
                 })
-            })
-            .and_then(|a| a.with_precision_and_scale(precision, scale))
+            })?
+            .with_precision_and_scale(precision, scale)
             .map(|a| Arc::new(a) as ArrayRef)
     }
 }
@@ -431,8 +416,8 @@ where
                         v
                     ))
                 })
-            })
-            .and_then(|a| a.with_precision_and_scale(precision, scale))
+            })?
+            .with_precision_and_scale(precision, scale)
             .map(|a| Arc::new(a) as ArrayRef)
     }
 }
@@ -724,16 +709,40 @@ pub fn cast_with_options(
             cast_primitive_to_list::<i64>(array, to, to_type, cast_options)
         }
         (Decimal128(_, s1), Decimal128(p2, s2)) => {
-            cast_decimal_to_decimal_with_option::<16, 16>(array, s1, p2, s2, cast_options)
+            cast_decimal_to_decimal::<Decimal128Type, Decimal128Type>(
+                as_primitive_array(array),
+                *s1,
+                *p2,
+                *s2,
+                cast_options,
+            )
         }
         (Decimal256(_, s1), Decimal256(p2, s2)) => {
-            cast_decimal_to_decimal_with_option::<32, 32>(array, s1, p2, s2, cast_options)
+            cast_decimal_to_decimal::<Decimal256Type, Decimal256Type>(
+                as_primitive_array(array),
+                *s1,
+                *p2,
+                *s2,
+                cast_options,
+            )
         }
         (Decimal128(_, s1), Decimal256(p2, s2)) => {
-            cast_decimal_to_decimal_with_option::<16, 32>(array, s1, p2, s2, cast_options)
+            cast_decimal_to_decimal::<Decimal128Type, Decimal256Type>(
+                as_primitive_array(array),
+                *s1,
+                *p2,
+                *s2,
+                cast_options,
+            )
         }
         (Decimal256(_, s1), Decimal128(p2, s2)) => {
-            cast_decimal_to_decimal_with_option::<32, 16>(array, s1, p2, s2, cast_options)
+            cast_decimal_to_decimal::<Decimal256Type, Decimal128Type>(
+                as_primitive_array(array),
+                *s1,
+                *p2,
+                *s2,
+                cast_options,
+            )
         }
         (Decimal128(_, scale), _) => {
             // cast decimal to other type
@@ -1964,471 +1973,110 @@ const fn time_unit_multiple(unit: &TimeUnit) -> i64 {
     }
 }
 
-/// Cast one type of decimal array to another type of decimal array
-fn cast_decimal_to_decimal_with_option<
-    const BYTE_WIDTH1: usize,
-    const BYTE_WIDTH2: usize,
->(
-    array: &ArrayRef,
-    input_scale: &i8,
-    output_precision: &u8,
-    output_scale: &i8,
-    cast_options: &CastOptions,
-) -> Result<ArrayRef, ArrowError> {
-    if cast_options.safe {
-        cast_decimal_to_decimal_safe::<BYTE_WIDTH1, BYTE_WIDTH2>(
-            array,
-            input_scale,
-            output_precision,
-            output_scale,
-        )
-    } else {
-        cast_decimal_to_decimal::<BYTE_WIDTH1, BYTE_WIDTH2>(
-            array,
-            input_scale,
-            output_precision,
-            output_scale,
-        )
+/// A utility trait that provides checked conversions between
+/// decimal types inspired by [`NumCast`]
+trait DecimalCast: Sized {
+    fn to_i128(self) -> Option<i128>;
+
+    fn to_i256(self) -> Option<i256>;
+
+    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self>;
+}
+
+impl DecimalCast for i128 {
+    fn to_i128(self) -> Option<i128> {
+        Some(self)
+    }
+
+    fn to_i256(self) -> Option<i256> {
+        Some(i256::from_i128(self))
+    }
+
+    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
+        n.to_i128()
     }
 }
 
-/// Cast one type of decimal array to another type of decimal array. Returning NULLs for
-/// the array values when cast failures happen.
-fn cast_decimal_to_decimal_safe<const BYTE_WIDTH1: usize, const BYTE_WIDTH2: usize>(
-    array: &ArrayRef,
-    input_scale: &i8,
-    output_precision: &u8,
-    output_scale: &i8,
-) -> Result<ArrayRef, ArrowError> {
-    if input_scale > output_scale {
-        // For example, input_scale is 4 and output_scale is 3;
-        // Original value is 11234_i128, and will be cast to 1123_i128.
-        let div = 10_i128.pow((input_scale - output_scale) as u32);
-        let half = div / 2;
-        let neg_half = half.wrapping_neg();
-        if BYTE_WIDTH1 == 16 {
-            let array = array.as_any().downcast_ref::<Decimal128Array>().unwrap();
-            if BYTE_WIDTH2 == 16 {
-                // rounding the result
-                let iter = array.iter().map(|v| {
-                    v.map(|v| {
-                        // the div must be gt_eq 10, we don't need to check the overflow for the `div`/`mod` operation
-                        let d = v.wrapping_div(div);
-                        let r = v.wrapping_rem(div);
-                        if v >= 0 && r >= half {
-                            d.wrapping_add(1)
-                        } else if v < 0 && r <= neg_half {
-                            d.wrapping_sub(1)
-                        } else {
-                            d
-                        }
-                    })
-                });
-                let casted_array = unsafe {
-                    PrimitiveArray::<Decimal128Type>::from_trusted_len_iter(iter)
-                };
-                casted_array
-                    .with_precision_and_scale(*output_precision, *output_scale)
-                    .map(|a| Arc::new(a) as ArrayRef)
-            } else {
-                let iter = array.iter().map(|v| {
-                    v.map(|v| {
-                        let d = v.wrapping_div(div);
-                        let r = v.wrapping_rem(div);
-                        i256::from_i128(if v >= 0 && r >= half {
-                            d.wrapping_add(1)
-                        } else if v < 0 && r <= neg_half {
-                            d.wrapping_sub(1)
-                        } else {
-                            d
-                        })
-                    })
-                });
-                let casted_array = unsafe {
-                    PrimitiveArray::<Decimal256Type>::from_trusted_len_iter(iter)
-                };
-                casted_array
-                    .with_precision_and_scale(*output_precision, *output_scale)
-                    .map(|a| Arc::new(a) as ArrayRef)
-            }
-        } else {
-            let array = array.as_any().downcast_ref::<Decimal256Array>().unwrap();
-            let div = i256::from_i128(div);
-            let half = div / i256::from_i128(2);
-            let neg_half = half.wrapping_neg();
-            if BYTE_WIDTH2 == 16 {
-                let iter = array.iter().map(|v| {
-                    v.and_then(|v| {
-                        let d = v.wrapping_div(div);
-                        let r = v.wrapping_rem(div);
-                        if v >= i256::ZERO && r >= half {
-                            d.wrapping_add(i256::ONE)
-                        } else if v < i256::ZERO && r <= neg_half {
-                            d.wrapping_sub(i256::ONE)
-                        } else {
-                            d
-                        }
-                        .to_i128()
-                    })
-                });
-                let casted_array = unsafe {
-                    PrimitiveArray::<Decimal128Type>::from_trusted_len_iter(iter)
-                };
-                casted_array
-                    .with_precision_and_scale(*output_precision, *output_scale)
-                    .map(|a| Arc::new(a) as ArrayRef)
-            } else {
-                let iter = array.iter().map(|v| {
-                    v.map(|v| {
-                        let d = v.wrapping_div(div);
-                        let r = v.wrapping_rem(div);
-                        if v >= i256::ZERO && r >= half {
-                            d.wrapping_add(i256::ONE)
-                        } else if v < i256::ZERO && r <= neg_half {
-                            d.wrapping_sub(i256::ONE)
-                        } else {
-                            d
-                        }
-                    })
-                });
-                let casted_array = unsafe {
-                    PrimitiveArray::<Decimal256Type>::from_trusted_len_iter(iter)
-                };
-                casted_array
-                    .with_precision_and_scale(*output_precision, *output_scale)
-                    .map(|a| Arc::new(a) as ArrayRef)
-            }
-        }
-    } else {
-        // For example, input_scale is 3 and output_scale is 4;
-        // Original value is 1123_i128, and will be cast to 11230_i128.
-        let mul = 10_i128.pow((output_scale - input_scale) as u32);
-        if BYTE_WIDTH1 == 16 {
-            let array = array.as_any().downcast_ref::<Decimal128Array>().unwrap();
-            if BYTE_WIDTH2 == 16 {
-                let iter = array
-                    .iter()
-                    .map(|v| v.and_then(|v| v.mul_checked(mul).ok()));
-                let casted_array = unsafe {
-                    PrimitiveArray::<Decimal128Type>::from_trusted_len_iter(iter)
-                };
-                casted_array
-                    .with_precision_and_scale(*output_precision, *output_scale)
-                    .map(|a| Arc::new(a) as ArrayRef)
-            } else {
-                let iter = array.iter().map(|v| {
-                    v.and_then(|v| v.mul_checked(mul).ok().map(i256::from_i128))
-                });
-                let casted_array = unsafe {
-                    PrimitiveArray::<Decimal256Type>::from_trusted_len_iter(iter)
-                };
-                casted_array
-                    .with_precision_and_scale(*output_precision, *output_scale)
-                    .map(|a| Arc::new(a) as ArrayRef)
-            }
-        } else {
-            let array = array.as_any().downcast_ref::<Decimal256Array>().unwrap();
-            let mul = i256::from_i128(mul);
-            if BYTE_WIDTH2 == 16 {
-                let iter = array.iter().map(|v| {
-                    v.and_then(|v| v.mul_checked(mul).ok().and_then(|v| v.to_i128()))
-                });
-                let casted_array = unsafe {
-                    PrimitiveArray::<Decimal128Type>::from_trusted_len_iter(iter)
-                };
-                casted_array
-                    .with_precision_and_scale(*output_precision, *output_scale)
-                    .map(|a| Arc::new(a) as ArrayRef)
-            } else {
-                let iter = array
-                    .iter()
-                    .map(|v| v.and_then(|v| v.mul_checked(mul).ok()));
-                let casted_array = unsafe {
-                    PrimitiveArray::<Decimal256Type>::from_trusted_len_iter(iter)
-                };
-                casted_array
-                    .with_precision_and_scale(*output_precision, *output_scale)
-                    .map(|a| Arc::new(a) as ArrayRef)
-            }
-        }
+impl DecimalCast for i256 {
+    fn to_i128(self) -> Option<i128> {
+        self.to_i128()
+    }
+
+    fn to_i256(self) -> Option<i256> {
+        Some(self)
+    }
+
+    fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
+        n.to_i256()
     }
 }
 
-/// Cast one type of decimal array to another type of decimal array. Returning `Err` if
-/// cast failure happens.
-fn cast_decimal_to_decimal<const BYTE_WIDTH1: usize, const BYTE_WIDTH2: usize>(
-    array: &ArrayRef,
-    input_scale: &i8,
-    output_precision: &u8,
-    output_scale: &i8,
-) -> Result<ArrayRef, ArrowError> {
-    if input_scale > output_scale {
-        // For example, input_scale is 4 and output_scale is 3;
-        // Original value is 11234_i128, and will be cast to 1123_i128.
-        if BYTE_WIDTH1 == 16 {
-            let array = array.as_any().downcast_ref::<Decimal128Array>().unwrap();
-            if BYTE_WIDTH2 == 16 {
-                // the div must be greater or equal than 10
-                let div = 10_i128
-                    .pow_checked((input_scale - output_scale) as u32)
-                    .map_err(|_| {
-                        ArrowError::CastError(format!(
-                            "Cannot cast. The scale {} causes overflow.",
-                            *output_scale,
-                        ))
-                    })?;
-                let half = div / 2;
-                let neg_half = -half;
+fn cast_decimal_to_decimal<I, O>(
+    array: &PrimitiveArray<I>,
+    input_scale: i8,
+    output_precision: u8,
+    output_scale: i8,
+    cast_options: &CastOptions,
+) -> Result<ArrayRef, ArrowError>
+where
+    I: DecimalType,
+    O: DecimalType,
+    I::Native: DecimalCast + ArrowNativeTypeOp,
+    O::Native: DecimalCast + ArrowNativeTypeOp,
+{
+    let error = |x| {
+        ArrowError::CastError(format!(
+            "Cannot cast to {}({}, {}). Overflowing on {:?}",
+            O::PREFIX,
+            output_precision,
+            output_scale,
+            x
+        ))
+    };
 
-                array
-                    .try_unary::<_, Decimal128Type, _>(|v| {
-                        // cast to smaller scale, need to round the result
-                        // the div must be gt_eq 10, we don't need to check the overflow for the `div`/`mod` operation
-                        let d = v.wrapping_div(div);
-                        let r = v.wrapping_rem(div);
-                        if v >= 0 && r >= half {
-                            d.checked_add(1)
-                        } else if v < 0 && r <= neg_half {
-                            d.checked_sub(1)
-                        } else {
-                            Some(d)
-                        }
-                        .ok_or_else(|| {
-                            ArrowError::CastError(format!(
-                                "Cannot cast to {:?}({}, {}). Overflowing on {:?}",
-                                Decimal128Type::PREFIX,
-                                *output_precision,
-                                *output_scale,
-                                v
-                            ))
-                        })
-                    })
-                    .and_then(|a| {
-                        a.with_precision_and_scale(*output_precision, *output_scale)
-                    })
-                    .map(|a| Arc::new(a) as ArrayRef)
-            } else {
-                let div = i256::from_i128(10_i128)
-                    .pow_checked((input_scale - output_scale) as u32)
-                    .map_err(|_| {
-                        ArrowError::CastError(format!(
-                            "Cannot cast. The scale {} causes overflow.",
-                            *output_scale,
-                        ))
-                    })?;
+    let array: PrimitiveArray<O> = if input_scale > output_scale {
+        let div = I::Native::from_decimal(10_i128)
+            .unwrap()
+            .pow_checked((input_scale - output_scale) as u32)?;
 
-                let half = div / i256::from_i128(2_i128);
-                let neg_half = -half;
+        let half = div.div_wrapping(I::Native::from_usize(2).unwrap());
+        let half_neg = half.neg_wrapping();
 
-                array
-                    .try_unary::<_, Decimal256Type, _>(|v| {
-                        // the div must be gt_eq 10, we don't need to check the overflow for the `div`/`mod` operation
-                        let v = i256::from_i128(v);
-                        let d = v.wrapping_div(div);
-                        let r = v.wrapping_rem(div);
-                        if v >= i256::ZERO && r >= half {
-                            d.checked_add(i256::ONE)
-                        } else if v < i256::ZERO && r <= neg_half {
-                            d.checked_sub(i256::ONE)
-                        } else {
-                            Some(d)
-                        }
-                        .ok_or_else(|| {
-                            ArrowError::CastError(format!(
-                                "Cannot cast to {:?}({}, {}). Overflowing on {:?}",
-                                Decimal256Type::PREFIX,
-                                *output_precision,
-                                *output_scale,
-                                v
-                            ))
-                        })
-                    })
-                    .and_then(|a| {
-                        a.with_precision_and_scale(*output_precision, *output_scale)
-                    })
-                    .map(|a| Arc::new(a) as ArrayRef)
-            }
-        } else {
-            let array = array.as_any().downcast_ref::<Decimal256Array>().unwrap();
-            let div = i256::from_i128(10_i128)
-                .pow_checked((input_scale - output_scale) as u32)
-                .map_err(|_| {
-                    ArrowError::CastError(format!(
-                        "Cannot cast. The scale {} causes overflow.",
-                        *output_scale,
-                    ))
-                })?;
-            let half = div / i256::from_i128(2_i128);
-            let neg_half = -half;
-            if BYTE_WIDTH2 == 16 {
-                array
-                    .try_unary::<_, Decimal128Type, _>(|v| {
-                        // the div must be gt_eq 10, we don't need to check the overflow for the `div`/`mod` operation
-                        let d = v.wrapping_div(div);
-                        let r = v.wrapping_rem(div);
-                        if v >= i256::ZERO && r >= half {
-                            d.checked_add(i256::ONE)
-                        } else if v < i256::ZERO && r <= neg_half {
-                            d.checked_sub(i256::ONE)
-                        } else {
-                            Some(d)
-                        }.ok_or_else(|| {
-                            ArrowError::CastError(format!(
-                                "Cannot cast to {:?}({}, {}). Overflowing on {:?}",
-                                Decimal128Type::PREFIX,
-                                *output_precision,
-                                *output_scale,
-                                v
-                            ))
-                        }).and_then(|v| v.to_i128().ok_or_else(|| {
-                            ArrowError::InvalidArgumentError(
-                                format!("{:?} cannot be casted to 128-bit integer for Decimal128", v),
-                            )
-                        }))
-                    })
-                    .and_then(|a| {
-                        a.with_precision_and_scale(*output_precision, *output_scale)
-                    })
-                    .map(|a| Arc::new(a) as ArrayRef)
-            } else {
-                array
-                    .try_unary::<_, Decimal256Type, _>(|v| {
-                        // the div must be gt_eq 10, we don't need to check the overflow for the `div`/`mod` operation
-                        let d = v.wrapping_div(div);
-                        let r = v.wrapping_rem(div);
-                        if v >= i256::ZERO && r >= half {
-                            d.checked_add(i256::ONE)
-                        } else if v < i256::ZERO && r <= neg_half {
-                            d.checked_sub(i256::ONE)
-                        } else {
-                            Some(d)
-                        }
-                        .ok_or_else(|| {
-                            ArrowError::CastError(format!(
-                                "Cannot cast to {:?}({}, {}). Overflowing on {:?}",
-                                Decimal256Type::PREFIX,
-                                *output_precision,
-                                *output_scale,
-                                v
-                            ))
-                        })
-                    })
-                    .and_then(|a| {
-                        a.with_precision_and_scale(*output_precision, *output_scale)
-                    })
-                    .map(|a| Arc::new(a) as ArrayRef)
-            }
+        let f = |x: I::Native| {
+            // div is >= 10 and so this cannot overflow
+            let div = x.div_wrapping(div);
+            let rem = x.mod_wrapping(div);
+
+            // Round result
+            let adjusted = match x >= I::Native::ZERO {
+                true if rem >= half => div.add_wrapping(I::Native::ONE),
+                false if rem <= half_neg => div.sub_wrapping(I::Native::ONE),
+                _ => div,
+            };
+            O::Native::from_decimal(adjusted)
+        };
+
+        match cast_options.safe {
+            true => array.unary_opt(f),
+            false => array.try_unary(|x| f(x).ok_or_else(|| error(x)))?,
         }
     } else {
-        // For example, input_scale is 3 and output_scale is 4;
-        // Original value is 1123_i128, and will be cast to 11230_i128.
-        if BYTE_WIDTH1 == 16 {
-            let array = array.as_any().downcast_ref::<Decimal128Array>().unwrap();
-
-            if BYTE_WIDTH2 == 16 {
-                let mul = 10_i128
-                    .pow_checked((output_scale - input_scale) as u32)
-                    .map_err(|_| {
-                        ArrowError::CastError(format!(
-                            "Cannot cast. The scale {} causes overflow.",
-                            *output_scale,
-                        ))
-                    })?;
+        let mul = O::Native::from_decimal(10_i128)
+            .unwrap()
+            .pow_checked((output_scale - input_scale) as u32)?;
 
-                array
-                    .try_unary::<_, Decimal128Type, _>(|v| {
-                        v.checked_mul(mul).ok_or_else(|| {
-                            ArrowError::CastError(format!(
-                                "Cannot cast to {:?}({}, {}). Overflowing on {:?}",
-                                Decimal128Type::PREFIX,
-                                *output_precision,
-                                *output_scale,
-                                v
-                            ))
-                        })
-                    })
-                    .and_then(|a| {
-                        a.with_precision_and_scale(*output_precision, *output_scale)
-                    })
-                    .map(|a| Arc::new(a) as ArrayRef)
-            } else {
-                let mul = i256::from_i128(10_i128)
-                    .pow_checked((output_scale - input_scale) as u32)
-                    .map_err(|_| {
-                        ArrowError::CastError(format!(
-                            "Cannot cast. The scale {} causes overflow.",
-                            *output_scale,
-                        ))
-                    })?;
+        let f = |x| O::Native::from_decimal(x).and_then(|x| x.mul_checked(mul).ok());
 
-                array
-                    .try_unary::<_, Decimal256Type, _>(|v| {
-                        i256::from_i128(v).checked_mul(mul).ok_or_else(|| {
-                            ArrowError::CastError(format!(
-                                "Cannot cast to {:?}({}, {}). Overflowing on {:?}",
-                                Decimal256Type::PREFIX,
-                                *output_precision,
-                                *output_scale,
-                                v
-                            ))
-                        })
-                    })
-                    .and_then(|a| {
-                        a.with_precision_and_scale(*output_precision, *output_scale)
-                    })
-                    .map(|a| Arc::new(a) as ArrayRef)
-            }
-        } else {
-            let array = array.as_any().downcast_ref::<Decimal256Array>().unwrap();
-            let mul = i256::from_i128(10_i128)
-                .pow_checked((output_scale - input_scale) as u32)
-                .map_err(|_| {
-                    ArrowError::CastError(format!(
-                        "Cannot cast. The scale {} causes overflow.",
-                        *output_scale,
-                    ))
-                })?;
-            if BYTE_WIDTH2 == 16 {
-                array
-                    .try_unary::<_, Decimal128Type, _>(|v| {
-                        v.checked_mul(mul).ok_or_else(|| {
-                            ArrowError::CastError(format!(
-                                "Cannot cast to {:?}({}, {}). Overflowing on {:?}",
-                                Decimal128Type::PREFIX,
-                                *output_precision,
-                                *output_scale,
-                                v
-                            ))
-                        }).and_then(|v| v.to_i128().ok_or_else(|| {
-                            ArrowError::InvalidArgumentError(
-                                format!("{:?} cannot be casted to 128-bit integer for Decimal128", v),
-                            )
-                        }))
-                    })
-                    .and_then(|a| {
-                        a.with_precision_and_scale(*output_precision, *output_scale)
-                    })
-                    .map(|a| Arc::new(a) as ArrayRef)
-            } else {
-                array
-                    .try_unary::<_, Decimal256Type, _>(|v| {
-                        v.checked_mul(mul).ok_or_else(|| {
-                            ArrowError::CastError(format!(
-                                "Cannot cast to {:?}({}, {}). Overflowing on {:?}",
-                                Decimal256Type::PREFIX,
-                                *output_precision,
-                                *output_scale,
-                                v
-                            ))
-                        })
-                    })
-                    .and_then(|a| {
-                        a.with_precision_and_scale(*output_precision, *output_scale)
-                    })
-                    .map(|a| Arc::new(a) as ArrayRef)
-            }
+        match cast_options.safe {
+            true => array.unary_opt(f),
+            false => array.try_unary(|x| f(x).ok_or_else(|| error(x)))?,
         }
-    }
+    };
+
+    Ok(Arc::new(array.with_precision_and_scale(
+        output_precision,
+        output_scale,
+    )?))
 }
 
 /// Convert Array into a PrimitiveArray of type, and apply numeric cast
@@ -3678,6 +3326,7 @@ mod tests {
             for (i, x) in $OUTPUT_VALUES.iter().enumerate() {
                 match x {
                     Some(x) => {
+                        assert!(!result_array.is_null(i));
                         assert_eq!(result_array.value(i), *x);
                     }
                     None => {
@@ -3733,7 +3382,7 @@ mod tests {
     #[test]
     #[cfg(not(feature = "force_validate"))]
     #[should_panic(
-        expected = "5789604461865809771178549250434395392663499233282028201972879200395656481997 cannot be casted to 128-bit integer for Decimal128"
+        expected = "Cannot cast to Decimal128(20, 3). Overflowing on 57896044618658097711785492504343953926634992332820282019728792003956564819967"
     )]
     fn test_cast_decimal_to_decimal_round_with_error() {
         // decimal256 to decimal128 overflow
@@ -3901,7 +3550,7 @@ mod tests {
         let array = Arc::new(input_decimal_array) as ArrayRef;
         let result =
             cast_with_options(&array, &output_type, &CastOptions { safe: false });
-        assert_eq!("Cast error: Cannot cast to \"Decimal128\"(38, 38). Overflowing on 170141183460469231731687303715884105727",
+        assert_eq!("Cast error: Cannot cast to Decimal128(38, 38). Overflowing on 170141183460469231731687303715884105727",
                    result.unwrap_err().to_string());
     }
 
@@ -3916,7 +3565,7 @@ mod tests {
         let array = Arc::new(input_decimal_array) as ArrayRef;
         let result =
             cast_with_options(&array, &output_type, &CastOptions { safe: false });
-        assert_eq!("Cast error: Cannot cast to \"Decimal256\"(76, 76). Overflowing on 170141183460469231731687303715884105727",
+        assert_eq!("Cast error: Cannot cast to Decimal256(76, 76). Overflowing on 170141183460469231731687303715884105727",
                    result.unwrap_err().to_string());
     }
 
@@ -3951,7 +3600,7 @@ mod tests {
         let array = Arc::new(input_decimal_array) as ArrayRef;
         let result =
             cast_with_options(&array, &output_type, &CastOptions { safe: false });
-        assert_eq!("Invalid argument error: 17014118346046923173168730371588410572700 cannot be casted to 128-bit integer for Decimal128",
+        assert_eq!("Cast error: Cannot cast to Decimal128(38, 7). Overflowing on 170141183460469231731687303715884105727",
                    result.unwrap_err().to_string());
     }
 
@@ -3965,7 +3614,7 @@ mod tests {
         let array = Arc::new(input_decimal_array) as ArrayRef;
         let result =
             cast_with_options(&array, &output_type, &CastOptions { safe: false });
-        assert_eq!("Cast error: Cannot cast to \"Decimal256\"(76, 55). Overflowing on 170141183460469231731687303715884105727",
+        assert_eq!("Cast error: Cannot cast to Decimal256(76, 55). Overflowing on 170141183460469231731687303715884105727",
                    result.unwrap_err().to_string());
     }
 
@@ -7331,4 +6980,25 @@ mod tests {
 
         assert_eq!("1300", decimal_arr.value_as_string(0));
     }
+
+    #[test]
+    fn test_cast_decimal128_to_decimal256_negative() {
+        let input_type = DataType::Decimal128(10, 3);
+        let output_type = DataType::Decimal256(10, 5);
+        assert!(can_cast_types(&input_type, &output_type));
+        let array = vec![Some(i128::MAX), Some(i128::MIN)];
+        let input_decimal_array = create_decimal_array(array, 10, 3).unwrap();
+        let array = Arc::new(input_decimal_array) as ArrayRef;
+
+        let hundred = i256::from_i128(100);
+        generate_cast_test_case!(
+            &array,
+            Decimal256Array,
+            &output_type,
+            vec![
+                Some(i256::from_i128(i128::MAX).mul_wrapping(hundred)),
+                Some(i256::from_i128(i128::MIN).mul_wrapping(hundred))
+            ]
+        );
+    }
 }