polars_core/chunked_array/logical/
decimal.rs1use std::borrow::Cow;
2
3use super::*;
4use crate::chunked_array::cast::cast_chunks;
5use crate::prelude::*;
6
7pub type DecimalChunked = Logical<DecimalType, Int128Type>;
8
9impl Int128Chunked {
10 #[inline]
11 pub fn into_decimal_unchecked(self, precision: Option<usize>, scale: usize) -> DecimalChunked {
12 unsafe { DecimalChunked::new_logical(self, DataType::Decimal(precision, Some(scale))) }
14 }
15
16 pub fn into_decimal(
17 self,
18 precision: Option<usize>,
19 scale: usize,
20 ) -> PolarsResult<DecimalChunked> {
21 if let Some(precision) = precision {
23 let precision_max = 10_i128.pow(precision as u32);
24 if let Some((min, max)) = self.min_max() {
25 let max_abs = max.abs().max(min.abs());
26 polars_ensure!(
27 max_abs < precision_max,
28 ComputeError: "decimal precision {} can't fit values with {} digits",
29 precision,
30 max_abs.to_string().len()
31 );
32 }
33 }
34 Ok(self.into_decimal_unchecked(precision, scale))
35 }
36}
37
38impl LogicalType for DecimalChunked {
39 fn dtype(&self) -> &DataType {
40 &self.dtype
41 }
42
43 #[inline]
44 fn get_any_value(&self, i: usize) -> PolarsResult<AnyValue<'_>> {
45 polars_ensure!(i < self.len(), oob = i, self.len());
46 Ok(unsafe { self.get_any_value_unchecked(i) })
47 }
48
49 #[inline]
50 unsafe fn get_any_value_unchecked(&self, i: usize) -> AnyValue<'_> {
51 match self.phys.get_unchecked(i) {
52 Some(v) => AnyValue::Decimal(v, self.scale()),
53 None => AnyValue::Null,
54 }
55 }
56
57 fn cast_with_options(
58 &self,
59 dtype: &DataType,
60 cast_options: CastOptions,
61 ) -> PolarsResult<Series> {
62 let mut dtype = Cow::Borrowed(dtype);
63 if let DataType::Decimal(to_precision, to_scale) = dtype.as_ref() {
64 let from_precision = self.precision();
65 let from_scale = self.scale();
66
67 let to_precision = to_precision.or(from_precision);
68 let to_scale = to_scale.unwrap_or(from_scale);
69
70 if to_precision == from_precision && to_scale == from_scale {
71 return Ok(self.clone().into_series());
72 }
73
74 dtype = Cow::Owned(DataType::Decimal(to_precision, Some(to_scale)));
75 }
76
77 let arrow_dtype = self.dtype().to_arrow(CompatLevel::newest());
78 let chunks = self
79 .physical()
80 .chunks
81 .iter()
82 .map(|arr| {
83 arr.as_any()
84 .downcast_ref::<PrimitiveArray<i128>>()
85 .unwrap()
86 .clone()
87 .to(arrow_dtype.clone())
88 .to_boxed()
89 })
90 .collect::<Vec<_>>();
91 let chunks = cast_chunks(&chunks, dtype.as_ref(), cast_options)?;
92 Series::try_from((self.name().clone(), chunks))
93 }
94}
95
96impl DecimalChunked {
97 pub fn precision(&self) -> Option<usize> {
98 match &self.dtype {
99 DataType::Decimal(precision, _) => *precision,
100 _ => unreachable!(),
101 }
102 }
103
104 pub fn scale(&self) -> usize {
105 match &self.dtype {
106 DataType::Decimal(_, scale) => scale.unwrap_or_else(|| unreachable!()),
107 _ => unreachable!(),
108 }
109 }
110
111 pub fn to_scale(&self, scale: usize) -> PolarsResult<Cow<'_, Self>> {
112 if self.scale() == scale {
113 return Ok(Cow::Borrowed(self));
114 }
115
116 let mut precision = self.precision();
117 if let Some(ref mut precision) = precision {
118 if self.scale() < scale {
119 *precision += scale;
120 *precision = (*precision).min(38);
121 }
122 }
123
124 let s = self.cast_with_options(
125 &DataType::Decimal(precision, Some(scale)),
126 CastOptions::NonStrict,
127 )?;
128 Ok(Cow::Owned(s.decimal().unwrap().clone()))
129 }
130}