Path: blob/main/crates/polars-compute/src/cast/decimal_to.rs
6939 views
use arrow::array::*;1use arrow::datatypes::ArrowDataType;2use arrow::types::NativeType;3use num_traits::{AsPrimitive, Float, NumCast};4use polars_error::PolarsResult;56#[inline]7fn decimal_to_decimal_impl<F: Fn(i128) -> Option<i128>>(8from: &PrimitiveArray<i128>,9op: F,10to_precision: usize,11to_scale: usize,12) -> PrimitiveArray<i128> {13let upper_bound_for_precision = 10_i128.saturating_pow(to_precision as u32);14let lower_bound_for_precision = upper_bound_for_precision.saturating_neg();1516let values = from.iter().map(|x| {17x.and_then(|x| {18op(*x).and_then(|x| {19if x >= upper_bound_for_precision || x <= lower_bound_for_precision {20None21} else {22Some(x)23}24})25})26});27PrimitiveArray::<i128>::from_trusted_len_iter(values)28.to(ArrowDataType::Decimal(to_precision, to_scale))29}3031/// Returns a [`PrimitiveArray<i128>`] with the cast values. Values are `None` on overflow32pub fn decimal_to_decimal(33from: &PrimitiveArray<i128>,34to_precision: usize,35to_scale: usize,36) -> PrimitiveArray<i128> {37let (from_precision, from_scale) =38if let ArrowDataType::Decimal(p, s) = from.dtype().to_logical_type() {39(*p, *s)40} else {41panic!("internal error: i128 is always a decimal")42};4344if to_scale == from_scale && to_precision >= from_precision {45// fast path46return from47.clone()48.to(ArrowDataType::Decimal(to_precision, to_scale));49}50// todo: other fast paths include increasing scale and precision by so that51// a number will never overflow (validity is preserved)5253if from_scale > to_scale {54let factor = 10_i128.pow((from_scale - to_scale) as u32);55decimal_to_decimal_impl(56from,57|x: i128| x.checked_div(factor),58to_precision,59to_scale,60)61} else {62let factor = 10_i128.pow((to_scale - from_scale) as u32);63decimal_to_decimal_impl(64from,65|x: i128| x.checked_mul(factor),66to_precision,67to_scale,68)69}70}7172pub(super) fn decimal_to_decimal_dyn(73from: &dyn Array,74to_precision: usize,75to_scale: usize,76) -> PolarsResult<Box<dyn Array>> {77let from = from.as_any().downcast_ref().unwrap();78Ok(Box::new(decimal_to_decimal(from, to_precision, to_scale)))79}8081/// Returns a [`PrimitiveArray<i128>`] with the cast values. Values are `None` on overflow82pub fn decimal_to_float<T>(from: &PrimitiveArray<i128>) -> PrimitiveArray<T>83where84T: NativeType + Float,85f64: AsPrimitive<T>,86{87let (_, from_scale) = if let ArrowDataType::Decimal(p, s) = from.dtype().to_logical_type() {88(*p, *s)89} else {90panic!("internal error: i128 is always a decimal")91};9293let div = 10_f64.powi(from_scale as i32);94let values = from95.values()96.iter()97.map(|x| (*x as f64 / div).as_())98.collect();99100PrimitiveArray::<T>::new(T::PRIMITIVE.into(), values, from.validity().cloned())101}102103pub(super) fn decimal_to_float_dyn<T>(from: &dyn Array) -> PolarsResult<Box<dyn Array>>104where105T: NativeType + Float,106f64: AsPrimitive<T>,107{108let from = from.as_any().downcast_ref().unwrap();109Ok(Box::new(decimal_to_float::<T>(from)))110}111112/// Returns a [`PrimitiveArray<i128>`] with the cast values. Values are `None` on overflow113pub fn decimal_to_integer<T>(from: &PrimitiveArray<i128>) -> PrimitiveArray<T>114where115T: NativeType + NumCast,116{117let (_, from_scale) = if let ArrowDataType::Decimal(p, s) = from.dtype().to_logical_type() {118(*p, *s)119} else {120panic!("internal error: i128 is always a decimal")121};122123let factor = 10_i128.pow(from_scale as u32);124let values = from.iter().map(|x| x.and_then(|x| T::from(*x / factor)));125126PrimitiveArray::from_trusted_len_iter(values)127}128129pub(super) fn decimal_to_integer_dyn<T>(from: &dyn Array) -> PolarsResult<Box<dyn Array>>130where131T: NativeType + NumCast,132{133let from = from.as_any().downcast_ref().unwrap();134Ok(Box::new(decimal_to_integer::<T>(from)))135}136137/// Returns a [`Utf8Array`] where every element is the utf8 representation of the decimal.138#[cfg(feature = "dtype-decimal")]139pub(super) fn decimal_to_utf8view(from: &PrimitiveArray<i128>) -> Utf8ViewArray {140use arrow::compute::decimal::DecimalFmtBuffer;141142let (_, from_scale) = if let ArrowDataType::Decimal(p, s) = from.dtype().to_logical_type() {143(*p, *s)144} else {145panic!("internal error: i128 is always a decimal")146};147148let mut mutable = MutableBinaryViewArray::with_capacity(from.len());149let mut fmt_buf = DecimalFmtBuffer::new();150for &x in from.values().iter() {151mutable.push_value_ignore_validity(fmt_buf.format(x, from_scale, false))152}153154mutable.freeze().with_validity(from.validity().cloned())155}156157#[cfg(feature = "dtype-decimal")]158pub(super) fn decimal_to_utf8view_dyn(from: &dyn Array) -> Utf8ViewArray {159let from = from.as_any().downcast_ref().unwrap();160decimal_to_utf8view(from)161}162163164