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