1use crate::cast::*;
19use crate::parse::{DecimalParseError, parse_decimal_checked};
20
21pub trait DecimalCast: Sized {
24 fn to_i32(self) -> Option<i32>;
26
27 fn to_i64(self) -> Option<i64>;
29
30 fn to_i128(self) -> Option<i128>;
32
33 fn to_i256(self) -> Option<i256>;
35
36 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self>;
38
39 fn from_f64(n: f64) -> Option<Self>;
41}
42
43impl DecimalCast for i32 {
44 fn to_i32(self) -> Option<i32> {
45 Some(self)
46 }
47
48 fn to_i64(self) -> Option<i64> {
49 Some(self as i64)
50 }
51
52 fn to_i128(self) -> Option<i128> {
53 Some(self as i128)
54 }
55
56 fn to_i256(self) -> Option<i256> {
57 Some(i256::from_i128(self as i128))
58 }
59
60 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
61 n.to_i32()
62 }
63
64 fn from_f64(n: f64) -> Option<Self> {
65 n.to_i32()
66 }
67}
68
69impl DecimalCast for i64 {
70 fn to_i32(self) -> Option<i32> {
71 i32::try_from(self).ok()
72 }
73
74 fn to_i64(self) -> Option<i64> {
75 Some(self)
76 }
77
78 fn to_i128(self) -> Option<i128> {
79 Some(self as i128)
80 }
81
82 fn to_i256(self) -> Option<i256> {
83 Some(i256::from_i128(self as i128))
84 }
85
86 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
87 n.to_i64()
88 }
89
90 fn from_f64(n: f64) -> Option<Self> {
91 num_traits::ToPrimitive::to_i64(&n)
94 }
95}
96
97impl DecimalCast for i128 {
98 fn to_i32(self) -> Option<i32> {
99 i32::try_from(self).ok()
100 }
101
102 fn to_i64(self) -> Option<i64> {
103 i64::try_from(self).ok()
104 }
105
106 fn to_i128(self) -> Option<i128> {
107 Some(self)
108 }
109
110 fn to_i256(self) -> Option<i256> {
111 Some(i256::from_i128(self))
112 }
113
114 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
115 n.to_i128()
116 }
117
118 fn from_f64(n: f64) -> Option<Self> {
119 n.to_i128()
120 }
121}
122
123impl DecimalCast for i256 {
124 fn to_i32(self) -> Option<i32> {
125 self.to_i128().map(|x| i32::try_from(x).ok())?
126 }
127
128 fn to_i64(self) -> Option<i64> {
129 self.to_i128().map(|x| i64::try_from(x).ok())?
130 }
131
132 fn to_i128(self) -> Option<i128> {
133 self.to_i128()
134 }
135
136 fn to_i256(self) -> Option<i256> {
137 Some(self)
138 }
139
140 fn from_decimal<T: DecimalCast>(n: T) -> Option<Self> {
141 n.to_i256()
142 }
143
144 fn from_f64(n: f64) -> Option<Self> {
145 i256::from_f64(n)
146 }
147}
148
149#[expect(clippy::type_complexity)]
162fn make_upscaler<I: DecimalType, O: DecimalType>(
163 input_precision: u8,
164 input_scale: i8,
165 output_precision: u8,
166 output_scale: i8,
167) -> Option<(
168 impl Fn(I::Native) -> Option<O::Native>,
169 Option<impl Fn(I::Native) -> O::Native>,
170)>
171where
172 I::Native: DecimalCast + ArrowNativeTypeOp,
173 O::Native: DecimalCast + ArrowNativeTypeOp,
174{
175 let delta_scale = output_scale as i16 - input_scale as i16;
176
177 let max = O::MAX_FOR_EACH_PRECISION.get(delta_scale as usize)?;
182 let mul = max.add_wrapping(O::Native::ONE);
183 let f_fallible = move |x| O::Native::from_decimal(x)?.mul_checked(mul).ok();
184
185 let is_infallible_cast = (input_precision as i16) + delta_scale <= (output_precision as i16);
192 let f_infallible = is_infallible_cast
193 .then_some(move |x| O::Native::from_decimal(x).unwrap().mul_wrapping(mul));
194 Some((f_fallible, f_infallible))
195}
196
197#[expect(clippy::type_complexity)]
211fn make_downscaler<I: DecimalType, O: DecimalType>(
212 input_precision: u8,
213 input_scale: i8,
214 output_precision: u8,
215 output_scale: i8,
216) -> Option<(
217 impl Fn(I::Native) -> Option<O::Native>,
218 Option<impl Fn(I::Native) -> O::Native>,
219)>
220where
221 I::Native: DecimalCast + ArrowNativeTypeOp,
222 O::Native: DecimalCast + ArrowNativeTypeOp,
223{
224 let delta_scale = input_scale as i16 - output_scale as i16;
225
226 let max = I::MAX_FOR_EACH_PRECISION.get(delta_scale as usize)?;
233
234 let div = max.add_wrapping(I::Native::ONE);
235 let half = div.div_wrapping(I::Native::ONE.add_wrapping(I::Native::ONE));
236 let half_neg = half.neg_wrapping();
237
238 let f_fallible = move |x: I::Native| {
239 let (d, r) = x.div_rem_wrapping(div);
241
242 let adjusted = match x >= I::Native::ZERO {
244 true if r >= half => d.add_wrapping(I::Native::ONE),
245 false if r <= half_neg => d.sub_wrapping(I::Native::ONE),
246 _ => d,
247 };
248 O::Native::from_decimal(adjusted)
249 };
250
251 let is_infallible_cast = (input_precision as i16) - delta_scale < (output_precision as i16);
262 let f_infallible = is_infallible_cast.then_some(move |x| f_fallible(x).unwrap());
263 Some((f_fallible, f_infallible))
264}
265
266fn apply_rescaler<I: DecimalType, O: DecimalType>(
270 value: I::Native,
271 output_precision: u8,
272 f: impl Fn(I::Native) -> Option<O::Native>,
273 f_infallible: Option<impl Fn(I::Native) -> O::Native>,
274) -> Option<O::Native>
275where
276 I::Native: DecimalCast,
277 O::Native: DecimalCast,
278{
279 if let Some(f_infallible) = f_infallible {
280 Some(f_infallible(value))
281 } else {
282 f(value).filter(|v| O::is_valid_decimal_precision(*v, output_precision))
283 }
284}
285
286pub fn rescale_decimal<I: DecimalType, O: DecimalType>(
302 value: I::Native,
303 input_precision: u8,
304 input_scale: i8,
305 output_precision: u8,
306 output_scale: i8,
307) -> Option<O::Native>
308where
309 I::Native: DecimalCast + ArrowNativeTypeOp,
310 O::Native: DecimalCast + ArrowNativeTypeOp,
311{
312 validate_decimal_precision_and_scale::<I>(input_precision, input_scale).ok()?;
313 validate_decimal_precision_and_scale::<O>(output_precision, output_scale).ok()?;
314
315 if input_scale <= output_scale {
316 let (f, f_infallible) =
317 make_upscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)?;
318 apply_rescaler::<I, O>(value, output_precision, f, f_infallible)
319 } else {
320 let Some((f, f_infallible)) =
321 make_downscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)
322 else {
323 return Some(O::Native::ZERO);
325 };
326 apply_rescaler::<I, O>(value, output_precision, f, f_infallible)
327 }
328}
329
330fn cast_decimal_to_decimal_error<I, O>(
331 output_precision: u8,
332 output_scale: i8,
333) -> impl Fn(<I as ArrowPrimitiveType>::Native) -> ArrowError
334where
335 I: DecimalType,
336 O: DecimalType,
337 I::Native: DecimalCast + ArrowNativeTypeOp,
338 O::Native: DecimalCast + ArrowNativeTypeOp,
339{
340 move |x: I::Native| {
341 ArrowError::CastError(format!(
342 "Cannot cast to {}({}, {}). Overflowing on {:?}",
343 O::PREFIX,
344 output_precision,
345 output_scale,
346 x
347 ))
348 }
349}
350
351fn apply_decimal_cast<I: DecimalType, O: DecimalType>(
352 array: &PrimitiveArray<I>,
353 output_precision: u8,
354 output_scale: i8,
355 f_fallible: impl Fn(I::Native) -> Option<O::Native>,
356 f_infallible: Option<impl Fn(I::Native) -> O::Native>,
357 cast_options: &CastOptions,
358) -> Result<PrimitiveArray<O>, ArrowError>
359where
360 I::Native: DecimalCast + ArrowNativeTypeOp,
361 O::Native: DecimalCast + ArrowNativeTypeOp,
362{
363 let array = if let Some(f_infallible) = f_infallible {
364 array.unary(f_infallible)
365 } else if cast_options.safe {
366 array.unary_opt(|x| {
367 f_fallible(x).filter(|v| O::is_valid_decimal_precision(*v, output_precision))
368 })
369 } else {
370 let error = cast_decimal_to_decimal_error::<I, O>(output_precision, output_scale);
371 array.try_unary(|x| {
372 let v = f_fallible(x).ok_or_else(|| error(x))?;
373 O::validate_decimal_precision(v, output_precision, output_scale).map(|()| v)
374 })?
375 };
376 Ok(array)
377}
378
379fn convert_to_smaller_scale_decimal<I, O>(
380 array: &PrimitiveArray<I>,
381 input_precision: u8,
382 input_scale: i8,
383 output_precision: u8,
384 output_scale: i8,
385 cast_options: &CastOptions,
386) -> Result<PrimitiveArray<O>, ArrowError>
387where
388 I: DecimalType,
389 O: DecimalType,
390 I::Native: DecimalCast + ArrowNativeTypeOp,
391 O::Native: DecimalCast + ArrowNativeTypeOp,
392{
393 if let Some((f_fallible, f_infallible)) =
394 make_downscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)
395 {
396 apply_decimal_cast(
397 array,
398 output_precision,
399 output_scale,
400 f_fallible,
401 f_infallible,
402 cast_options,
403 )
404 } else {
405 let zeros = vec![O::Native::ZERO; array.len()];
407 Ok(PrimitiveArray::new(zeros.into(), array.nulls().cloned()))
408 }
409}
410
411fn convert_to_bigger_or_equal_scale_decimal<I, O>(
412 array: &PrimitiveArray<I>,
413 input_precision: u8,
414 input_scale: i8,
415 output_precision: u8,
416 output_scale: i8,
417 cast_options: &CastOptions,
418) -> Result<PrimitiveArray<O>, ArrowError>
419where
420 I: DecimalType,
421 O: DecimalType,
422 I::Native: DecimalCast + ArrowNativeTypeOp,
423 O::Native: DecimalCast + ArrowNativeTypeOp,
424{
425 if let Some((f, f_infallible)) =
426 make_upscaler::<I, O>(input_precision, input_scale, output_precision, output_scale)
427 {
428 apply_decimal_cast(
429 array,
430 output_precision,
431 output_scale,
432 f,
433 f_infallible,
434 cast_options,
435 )
436 } else {
437 Err(ArrowError::CastError(format!(
439 "Cannot cast to {}({}, {}). Value overflows for output scale",
440 O::PREFIX,
441 output_precision,
442 output_scale
443 )))
444 }
445}
446
447pub(crate) fn cast_decimal_to_decimal_same_type<T>(
449 array: &PrimitiveArray<T>,
450 input_precision: u8,
451 input_scale: i8,
452 output_precision: u8,
453 output_scale: i8,
454 cast_options: &CastOptions,
455) -> Result<ArrayRef, ArrowError>
456where
457 T: DecimalType,
458 T::Native: DecimalCast + ArrowNativeTypeOp,
459{
460 let array: PrimitiveArray<T> =
461 if input_scale == output_scale && input_precision <= output_precision {
462 array.clone()
463 } else if input_scale <= output_scale {
464 convert_to_bigger_or_equal_scale_decimal::<T, T>(
465 array,
466 input_precision,
467 input_scale,
468 output_precision,
469 output_scale,
470 cast_options,
471 )?
472 } else {
473 convert_to_smaller_scale_decimal::<T, T>(
475 array,
476 input_precision,
477 input_scale,
478 output_precision,
479 output_scale,
480 cast_options,
481 )?
482 };
483
484 Ok(Arc::new(array.with_precision_and_scale(
485 output_precision,
486 output_scale,
487 )?))
488}
489
490pub(crate) fn cast_decimal_to_decimal<I, O>(
492 array: &PrimitiveArray<I>,
493 input_precision: u8,
494 input_scale: i8,
495 output_precision: u8,
496 output_scale: i8,
497 cast_options: &CastOptions,
498) -> Result<ArrayRef, ArrowError>
499where
500 I: DecimalType,
501 O: DecimalType,
502 I::Native: DecimalCast + ArrowNativeTypeOp,
503 O::Native: DecimalCast + ArrowNativeTypeOp,
504{
505 let array: PrimitiveArray<O> = if input_scale > output_scale {
506 convert_to_smaller_scale_decimal::<I, O>(
507 array,
508 input_precision,
509 input_scale,
510 output_precision,
511 output_scale,
512 cast_options,
513 )?
514 } else {
515 convert_to_bigger_or_equal_scale_decimal::<I, O>(
516 array,
517 input_precision,
518 input_scale,
519 output_precision,
520 output_scale,
521 cast_options,
522 )?
523 };
524
525 Ok(Arc::new(array.with_precision_and_scale(
526 output_precision,
527 output_scale,
528 )?))
529}
530
531#[deprecated(
540 since = "60.0.0",
541 note = "Use `arrow_cast::parse::parse_decimal` instead"
542)]
543pub fn parse_string_to_decimal_native<T: DecimalType>(
544 value_str: &str,
545 scale: usize,
546) -> Result<T::Native, ArrowError> {
547 let overflow = || {
548 ArrowError::InvalidArgumentError(format!(
549 "Cannot convert {value_str} to {}: Overflow",
550 T::PREFIX
551 ))
552 };
553 let scale = i8::try_from(scale).map_err(|_| overflow())?;
554 parse_decimal_checked::<T>(value_str, T::MAX_PRECISION, scale).map_err(|e| match e {
555 DecimalParseError::InvalidFormat => {
556 ArrowError::InvalidArgumentError(format!("Invalid decimal format: {value_str:?}"))
557 }
558 DecimalParseError::Overflow => overflow(),
559 })
560}
561
562pub(crate) fn generic_string_to_decimal_cast<'a, T, S>(
563 from: &'a S,
564 precision: u8,
565 scale: i8,
566 cast_options: &CastOptions,
567) -> Result<PrimitiveArray<T>, ArrowError>
568where
569 T: DecimalType,
570 &'a S: StringArrayType<'a>,
571{
572 if cast_options.safe {
573 let iter = from
574 .iter()
575 .map(|v| parse_decimal_checked::<T>(v?, precision, scale).ok());
576 Ok(unsafe {
582 PrimitiveArray::<T>::from_trusted_len_iter(iter)
583 .with_precision_and_scale(precision, scale)?
584 })
585 } else {
586 let mut builder = PrimitiveBuilder::<T>::with_capacity(from.len());
587 for v in from.iter() {
588 match v {
589 Some(v) => {
590 let v = parse_decimal_checked::<T>(v, precision, scale).map_err(|e| {
591 let reason = match e {
592 DecimalParseError::InvalidFormat => "invalid decimal format",
593 DecimalParseError::Overflow => "value does not fit",
594 };
595 ArrowError::CastError(format!(
596 "Cannot cast string '{v}' to value of {}({precision}, {scale}) type: {reason}",
597 T::PREFIX,
598 ))
599 })?;
600 builder.append_value(v);
601 }
602 None => builder.append_null(),
603 }
604 }
605 builder.finish().with_precision_and_scale(precision, scale)
606 }
607}
608
609pub(crate) fn string_to_decimal_cast<T: DecimalType, Offset: OffsetSizeTrait>(
610 from: &GenericStringArray<Offset>,
611 precision: u8,
612 scale: i8,
613 cast_options: &CastOptions,
614) -> Result<PrimitiveArray<T>, ArrowError> {
615 generic_string_to_decimal_cast::<T, GenericStringArray<Offset>>(
616 from,
617 precision,
618 scale,
619 cast_options,
620 )
621}
622
623pub(crate) fn string_view_to_decimal_cast<T: DecimalType>(
624 from: &StringViewArray,
625 precision: u8,
626 scale: i8,
627 cast_options: &CastOptions,
628) -> Result<PrimitiveArray<T>, ArrowError> {
629 generic_string_to_decimal_cast::<T, StringViewArray>(from, precision, scale, cast_options)
630}
631
632pub(crate) fn cast_string_to_decimal<T: DecimalType, Offset: OffsetSizeTrait>(
634 from: &dyn Array,
635 precision: u8,
636 scale: i8,
637 cast_options: &CastOptions,
638) -> Result<ArrayRef, ArrowError> {
639 validate_decimal_precision_and_scale::<T>(precision, scale)?;
640
641 let result = match from.data_type() {
642 DataType::Utf8View => string_view_to_decimal_cast::<T>(
643 from.as_any().downcast_ref::<StringViewArray>().unwrap(),
644 precision,
645 scale,
646 cast_options,
647 )?,
648 DataType::Utf8 | DataType::LargeUtf8 => string_to_decimal_cast::<T, Offset>(
649 from.as_any()
650 .downcast_ref::<GenericStringArray<Offset>>()
651 .unwrap(),
652 precision,
653 scale,
654 cast_options,
655 )?,
656 other => {
657 return Err(ArrowError::ComputeError(format!(
658 "Cannot cast {other:?} to decimal",
659 )));
660 }
661 };
662
663 Ok(Arc::new(result))
664}
665
666pub(crate) fn cast_floating_point_to_decimal<T: ArrowPrimitiveType, D>(
667 array: &PrimitiveArray<T>,
668 precision: u8,
669 scale: i8,
670 cast_options: &CastOptions,
671) -> Result<ArrayRef, ArrowError>
672where
673 <T as ArrowPrimitiveType>::Native: AsPrimitive<f64>,
674 D: DecimalType + ArrowPrimitiveType,
675 <D as ArrowPrimitiveType>::Native: DecimalCast,
676{
677 validate_decimal_precision_and_scale::<D>(precision, scale)?;
678 let mul = 10_f64.powi(scale as i32);
679
680 if cast_options.safe {
681 array
682 .unary_opt::<_, D>(|v| float_to_decimal_checked::<D>(v.as_(), mul, precision).ok())
683 .with_precision_and_scale(precision, scale)
684 .map(|a| Arc::new(a) as ArrayRef)
685 } else {
686 array
687 .try_unary::<_, D, _>(|v| {
688 float_to_decimal_checked::<D>(v.as_(), mul, precision)
689 .map_err(|e| e.into_arrow_error(v, precision, scale))
690 })?
691 .with_precision_and_scale(precision, scale)
692 .map(|a| Arc::new(a) as ArrayRef)
693 }
694}
695
696#[inline]
711pub fn float_to_decimal<D>(input: f64, precision: u8, scale: i8) -> Result<D::Native, ArrowError>
712where
713 D: DecimalType,
714 D::Native: DecimalCast,
715{
716 validate_decimal_precision_and_scale::<D>(precision, scale)?;
717 float_to_decimal_checked::<D>(input, 10_f64.powi(scale as i32), precision)
718 .map_err(|e| e.into_arrow_error(input, precision, scale))
719}
720
721pub fn float_to_decimal_opt<D>(input: f64, precision: u8, scale: i8) -> Option<D::Native>
732where
733 D: DecimalType,
734 D::Native: DecimalCast,
735{
736 validate_decimal_precision_and_scale::<D>(precision, scale).ok()?;
737 float_to_decimal_checked::<D>(input, 10_f64.powi(scale as i32), precision).ok()
738}
739
740enum FloatToDecimalError<D: DecimalType> {
742 NativeOverflow,
743 PrecisionOverflow(D::Native),
744}
745
746impl<D: DecimalType> FloatToDecimalError<D> {
747 fn into_arrow_error(self, input: impl std::fmt::Debug, precision: u8, scale: i8) -> ArrowError {
748 match self {
749 Self::NativeOverflow => ArrowError::CastError(format!(
750 "Cannot cast to {}({}, {}). Overflowing on {:?}",
751 D::PREFIX,
752 precision,
753 scale,
754 input
755 )),
756 Self::PrecisionOverflow(value) => {
757 D::validate_decimal_precision(value, precision, scale).unwrap_err()
758 }
759 }
760 }
761}
762
763#[inline]
765fn float_to_decimal_checked<D>(
766 input: f64,
767 mul: f64,
768 precision: u8,
769) -> Result<D::Native, FloatToDecimalError<D>>
770where
771 D: DecimalType,
772 D::Native: DecimalCast,
773{
774 let value =
775 D::Native::from_f64((mul * input).round()).ok_or(FloatToDecimalError::NativeOverflow)?;
776 if D::is_valid_decimal_precision(value, precision) {
777 Ok(value)
778 } else {
779 Err(FloatToDecimalError::PrecisionOverflow(value))
780 }
781}
782
783#[deprecated(since = "60.0.0", note = "Use `float_to_decimal` instead")]
787#[inline(always)]
788pub fn single_float_to_decimal<D>(input: f64, mul: f64) -> Option<D::Native>
789where
790 D: DecimalType + ArrowPrimitiveType,
791 <D as ArrowPrimitiveType>::Native: DecimalCast,
792{
793 D::Native::from_f64((mul * input).round())
794}
795
796pub(crate) fn cast_decimal_to_integer<D, T>(
797 array: &dyn Array,
798 scale: i8,
799 cast_options: &CastOptions,
800) -> Result<ArrayRef, ArrowError>
801where
802 T: ArrowPrimitiveType,
803 <T as ArrowPrimitiveType>::Native: NumCast,
804 D: DecimalType + ArrowPrimitiveType,
805 <D as ArrowPrimitiveType>::Native: ToPrimitive,
806{
807 let array = array.as_primitive::<D>();
808
809 let div: D::Native = D::Native::usize_as(10)
811 .pow_checked(scale.unsigned_abs() as u32)
812 .map_err(|_| {
813 ArrowError::CastError(format!(
814 "Cannot cast to {:?}. The scale {} causes overflow.",
815 D::PREFIX,
816 scale,
817 ))
818 })?;
819
820 let mut value_builder = PrimitiveBuilder::<T>::with_capacity(array.len());
821
822 if scale < 0 {
823 match cast_options.safe {
824 true => {
825 for i in 0..array.len() {
826 if array.is_null(i) {
827 value_builder.append_null();
828 } else {
829 let v = array
830 .value(i)
831 .mul_checked(div)
832 .ok()
833 .and_then(<T::Native as NumCast>::from::<D::Native>);
834 value_builder.append_option(v);
835 }
836 }
837 }
838 false => {
839 for i in 0..array.len() {
840 if array.is_null(i) {
841 value_builder.append_null();
842 } else {
843 let v = array.value(i).mul_checked(div)?;
844
845 let value =
846 <T::Native as NumCast>::from::<D::Native>(v).ok_or_else(|| {
847 ArrowError::CastError(format!(
848 "value of {:?} is out of range {}",
849 v,
850 T::DATA_TYPE
851 ))
852 })?;
853
854 value_builder.append_value(value);
855 }
856 }
857 }
858 }
859 } else {
860 match cast_options.safe {
861 true => {
862 for i in 0..array.len() {
863 if array.is_null(i) {
864 value_builder.append_null();
865 } else {
866 let v = array.value(i).div_wrapping(div);
867 let v = <T::Native as NumCast>::from(v);
868 value_builder.append_option(v);
869 }
870 }
871 }
872 false => {
873 for i in 0..array.len() {
874 if array.is_null(i) {
875 value_builder.append_null();
876 } else {
877 let v = array.value(i).div_wrapping(div);
878
879 let value =
880 <T::Native as NumCast>::from::<D::Native>(v).ok_or_else(|| {
881 ArrowError::CastError(format!(
882 "value of {:?} is out of range {}",
883 v,
884 T::DATA_TYPE
885 ))
886 })?;
887
888 value_builder.append_value(value);
889 }
890 }
891 }
892 }
893 }
894 Ok(Arc::new(value_builder.finish()))
895}
896
897pub(crate) fn cast_decimal_to_float<D: DecimalType, T: ArrowPrimitiveType, F>(
903 array: &dyn Array,
904 op: F,
905) -> Result<ArrayRef, ArrowError>
906where
907 F: Fn(D::Native) -> T::Native,
908{
909 let array = array.as_primitive::<D>();
910 let array = array.unary::<_, T>(op);
911 Ok(Arc::new(array))
912}
913
914#[cfg(test)]
915mod tests {
916 use super::*;
917
918 #[test]
919 fn test_float_to_decimal() {
920 fn check<D: DecimalType>()
921 where
922 D::Native: DecimalCast,
923 {
924 for (input, scale, expected) in [
925 (12.345, 2, 1235),
926 (-12.345, 2, -1235),
927 (999.99, 2, 99999),
928 (-999.99, 2, -99999),
929 (99999.25, 0, 99999),
930 (999992.5, -1, 99999),
931 (0.0, 2, 0),
932 ] {
933 assert_eq!(
934 float_to_decimal_opt::<D>(input, 5, scale),
935 float_to_decimal::<D>(input, 5, scale).ok(),
936 );
937 assert_eq!(
938 float_to_decimal::<D>(input, 5, scale)
939 .unwrap()
940 .to_i128()
941 .unwrap(),
942 expected,
943 );
944 }
945 for (input, scale) in [
946 (12345.678, 2),
947 (-12345.678, 2),
948 (999.9975, 2),
949 (-999.9975, 2),
950 (99999.75, 0),
951 (-99999.75, 0),
952 (999997.5, -1),
953 (-999997.5, -1),
954 (f64::NAN, 0),
955 (f64::INFINITY, 0),
956 (f64::NEG_INFINITY, 0),
957 (f64::MAX, 0),
958 ] {
959 assert!(float_to_decimal::<D>(input, 5, scale).is_err());
960 assert!(float_to_decimal_opt::<D>(input, 5, scale).is_none());
961 }
962 for (precision, scale) in [(0, 0), (D::MAX_PRECISION + 1, 0), (5, 6)] {
963 assert!(float_to_decimal::<D>(0.0, precision, scale).is_err());
964 assert!(float_to_decimal_opt::<D>(0.0, precision, scale).is_none());
965 }
966 }
967 check::<Decimal32Type>();
968 check::<Decimal64Type>();
969 check::<Decimal128Type>();
970 check::<Decimal256Type>();
971 }
972
973 #[test]
974 #[expect(deprecated)]
975 fn test_single_float_to_decimal_preserves_native_range() {
976 for (input, expected) in [
979 (1_000_000_000.0, 1_000_000_000),
980 (-1_000_000_000.0, -1_000_000_000),
981 (999_999_999.75, 1_000_000_000),
982 (-999_999_999.75, -1_000_000_000),
983 ] {
984 assert_eq!(
985 single_float_to_decimal::<Decimal32Type>(input, 1.0),
986 Some(expected)
987 );
988 assert!(float_to_decimal::<Decimal32Type>(input, 9, 0).is_err());
989 }
990 assert_eq!(
991 single_float_to_decimal::<Decimal32Type>(999_999_999.0, 1.0),
992 Some(999_999_999),
993 );
994 assert_eq!(
995 single_float_to_decimal::<Decimal32Type>(12.345, 100.0),
996 Some(1235)
997 );
998 assert_eq!(
999 single_float_to_decimal::<Decimal32Type>(f64::MAX, 1.0),
1000 None
1001 );
1002 }
1003
1004 #[test]
1005 #[expect(deprecated)]
1006 fn test_parse_string_to_decimal_native() {
1007 assert_eq!(
1008 parse_string_to_decimal_native::<Decimal128Type>("123.456", 2).unwrap(),
1009 12346
1010 );
1011 assert_eq!(
1014 parse_string_to_decimal_native::<Decimal128Type>(&"9".repeat(38), 0).unwrap(),
1015 10_i128.pow(38) - 1
1016 );
1017 assert!(
1018 parse_string_to_decimal_native::<Decimal128Type>(&i128::MAX.to_string(), 0).is_err()
1019 );
1020 assert_eq!(
1021 parse_string_to_decimal_native::<Decimal128Type>("abc", 2)
1022 .unwrap_err()
1023 .to_string(),
1024 "Invalid argument error: Invalid decimal format: \"abc\""
1025 );
1026 assert_eq!(
1027 parse_string_to_decimal_native::<Decimal32Type>("1", 10)
1028 .unwrap_err()
1029 .to_string(),
1030 "Invalid argument error: Cannot convert 1 to Decimal32: Overflow"
1031 );
1032 }
1033
1034 #[test]
1035 fn test_rescale_decimal_upscale_within_precision() {
1036 let result = rescale_decimal::<Decimal128Type, Decimal128Type>(
1037 12_345_i128, 5,
1039 2,
1040 8,
1041 5,
1042 );
1043 assert_eq!(result, Some(12_345_000_i128));
1044 }
1045
1046 #[test]
1047 fn test_rescale_decimal_downscale_rounds_half_away_from_zero() {
1048 let positive = rescale_decimal::<Decimal128Type, Decimal128Type>(
1049 1_050_i128, 5, 3, 5, 1,
1051 );
1052 assert_eq!(positive, Some(11_i128)); let negative = rescale_decimal::<Decimal128Type, Decimal128Type>(
1055 -1_050_i128, 5,
1057 3,
1058 5,
1059 1,
1060 );
1061 assert_eq!(negative, Some(-11_i128)); }
1063
1064 #[test]
1065 fn test_rescale_decimal_downscale_large_delta_returns_zero() {
1066 let result = rescale_decimal::<Decimal32Type, Decimal32Type>(12_345_i32, 9, 9, 9, 4);
1067 assert_eq!(result, Some(0_i32));
1068 }
1069
1070 #[test]
1071 fn test_rescale_decimal_upscale_overflow_returns_none() {
1072 let result = rescale_decimal::<Decimal32Type, Decimal32Type>(9_999_i32, 4, 0, 5, 2);
1073 assert_eq!(result, None);
1074 }
1075
1076 #[test]
1077 fn test_rescale_decimal256_extreme_scales() {
1078 assert_eq!(
1079 rescale_decimal::<Decimal256Type, Decimal256Type>(i256::ONE, 76, -76, 76, 0),
1080 None
1081 );
1082 assert_eq!(
1083 rescale_decimal::<Decimal256Type, Decimal256Type>(i256::ZERO, 76, -76, 76, 0),
1084 Some(i256::ZERO)
1085 );
1086 assert_eq!(
1087 rescale_decimal::<Decimal256Type, Decimal256Type>(i256::ONE, 76, i8::MIN, 76, 76),
1088 None
1089 );
1090 assert_eq!(
1091 rescale_decimal::<Decimal256Type, Decimal256Type>(i256::ONE, 76, 76, 76, i8::MIN),
1092 Some(i256::ZERO)
1093 );
1094 }
1095
1096 #[test]
1097 fn test_rescale_decimal_invalid_input_precision_scale_returns_none() {
1098 let result = rescale_decimal::<Decimal128Type, Decimal128Type>(123_i128, 39, 39, 38, 38);
1099 assert_eq!(result, None);
1100 }
1101
1102 #[test]
1103 fn test_rescale_decimal_invalid_output_precision_scale_returns_none() {
1104 let result = rescale_decimal::<Decimal128Type, Decimal128Type>(123_i128, 38, 38, 39, 39);
1105 assert_eq!(result, None);
1106 }
1107}