1use arrow_schema::ArrowError;
18use std::fmt;
19
20macro_rules! decimal_try_new {
22 ($integer:ident, $scale:ident) => {{
23 if $scale > Self::MAX_PRECISION {
25 return Err(ArrowError::InvalidArgumentError(format!(
26 "Scale {} is larger than max precision {}",
27 $scale,
28 Self::MAX_PRECISION,
29 )));
30 }
31
32 if $integer.unsigned_abs() > Self::MAX_UNSCALED_VALUE {
34 return Err(ArrowError::InvalidArgumentError(format!(
35 "{} is wider than max precision {}",
36 $integer,
37 Self::MAX_PRECISION
38 )));
39 }
40
41 Ok(Self { $integer, $scale })
42 }};
43}
44
45macro_rules! format_decimal {
47 ($f:expr, $integer:expr, $scale:expr, $int_type:ty) => {{
48 let integer = if $scale == 0 {
49 $integer
50 } else {
51 let divisor = <$int_type>::pow(10, $scale as u32);
52 let remainder = $integer % divisor;
53 if remainder != 0 {
54 let sign = if $integer < 0 { "-" } else { "" };
56 let remainder = format!("{:0width$}", remainder.abs(), width = $scale as usize);
58 let remainder = remainder.trim_end_matches('0');
59 let quotient = $integer / divisor;
60 return write!($f, "{}{}.{}", sign, quotient.abs(), remainder);
61 }
62 $integer / divisor
63 };
64 write!($f, "{}", integer)
65 }};
66}
67
68#[derive(Debug, Clone, Copy, PartialEq)]
83pub struct VariantDecimal4 {
84 integer: i32,
85 scale: u8,
86}
87
88impl VariantDecimal4 {
89 pub(crate) const MAX_PRECISION: u8 = 9;
90 pub(crate) const MAX_UNSCALED_VALUE: u32 = u32::pow(10, Self::MAX_PRECISION as u32) - 1;
91
92 pub fn try_new(integer: i32, scale: u8) -> Result<Self, ArrowError> {
93 decimal_try_new!(integer, scale)
94 }
95
96 pub fn integer(&self) -> i32 {
100 self.integer
101 }
102
103 pub fn scale(&self) -> u8 {
107 self.scale
108 }
109}
110
111impl fmt::Display for VariantDecimal4 {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 format_decimal!(f, self.integer, self.scale, i32)
114 }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq)]
133pub struct VariantDecimal8 {
134 integer: i64,
135 scale: u8,
136}
137
138impl VariantDecimal8 {
139 pub(crate) const MAX_PRECISION: u8 = 18;
140 pub(crate) const MAX_UNSCALED_VALUE: u64 = u64::pow(10, Self::MAX_PRECISION as u32) - 1;
141
142 pub fn try_new(integer: i64, scale: u8) -> Result<Self, ArrowError> {
143 decimal_try_new!(integer, scale)
144 }
145
146 pub fn integer(&self) -> i64 {
150 self.integer
151 }
152
153 pub fn scale(&self) -> u8 {
157 self.scale
158 }
159}
160
161impl fmt::Display for VariantDecimal8 {
162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
163 format_decimal!(f, self.integer, self.scale, i64)
164 }
165}
166
167#[derive(Debug, Clone, Copy, PartialEq)]
183pub struct VariantDecimal16 {
184 integer: i128,
185 scale: u8,
186}
187
188impl VariantDecimal16 {
189 const MAX_PRECISION: u8 = 38;
190 const MAX_UNSCALED_VALUE: u128 = u128::pow(10, Self::MAX_PRECISION as u32) - 1;
191
192 pub fn try_new(integer: i128, scale: u8) -> Result<Self, ArrowError> {
193 decimal_try_new!(integer, scale)
194 }
195
196 pub fn integer(&self) -> i128 {
200 self.integer
201 }
202
203 pub fn scale(&self) -> u8 {
207 self.scale
208 }
209}
210
211impl fmt::Display for VariantDecimal16 {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 format_decimal!(f, self.integer, self.scale, i128)
214 }
215}
216
217macro_rules! impl_from_decimal_for_decimal {
219 ($from_ty:ty, $for_ty:ty) => {
220 impl From<$from_ty> for $for_ty {
221 fn from(decimal: $from_ty) -> Self {
222 Self {
223 integer: decimal.integer.into(),
224 scale: decimal.scale,
225 }
226 }
227 }
228 };
229}
230
231impl_from_decimal_for_decimal!(VariantDecimal4, VariantDecimal8);
232impl_from_decimal_for_decimal!(VariantDecimal4, VariantDecimal16);
233impl_from_decimal_for_decimal!(VariantDecimal8, VariantDecimal16);
234
235macro_rules! impl_try_from_decimal_for_decimal {
237 ($from_ty:ty, $for_ty:ty) => {
238 impl TryFrom<$from_ty> for $for_ty {
239 type Error = ArrowError;
240
241 fn try_from(decimal: $from_ty) -> Result<Self, ArrowError> {
242 let Ok(integer) = decimal.integer.try_into() else {
243 return Err(ArrowError::InvalidArgumentError(format!(
244 "Value {} is wider than max precision {}",
245 decimal.integer,
246 Self::MAX_PRECISION
247 )));
248 };
249 Self::try_new(integer, decimal.scale)
250 }
251 }
252 };
253}
254
255impl_try_from_decimal_for_decimal!(VariantDecimal8, VariantDecimal4);
256impl_try_from_decimal_for_decimal!(VariantDecimal16, VariantDecimal4);
257impl_try_from_decimal_for_decimal!(VariantDecimal16, VariantDecimal8);
258
259macro_rules! impl_try_from_int_for_decimal {
261 ($from_ty:ty, $for_ty:ty) => {
262 impl TryFrom<$from_ty> for $for_ty {
263 type Error = ArrowError;
264
265 fn try_from(integer: $from_ty) -> Result<Self, ArrowError> {
266 Self::try_new(integer, 0)
267 }
268 }
269 };
270}
271
272impl_try_from_int_for_decimal!(i32, VariantDecimal4);
273impl_try_from_int_for_decimal!(i64, VariantDecimal8);
274impl_try_from_int_for_decimal!(i128, VariantDecimal16);
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279
280 #[test]
281 fn test_variant_decimal_invalid_precision() {
282 let decimal4_too_large = VariantDecimal4::try_new(1_000_000_000_i32, 2);
284 assert!(
285 decimal4_too_large.is_err(),
286 "Decimal4 precision overflow should fail"
287 );
288 assert!(decimal4_too_large
289 .unwrap_err()
290 .to_string()
291 .contains("wider than max precision"));
292
293 let decimal4_too_small = VariantDecimal4::try_new(-1_000_000_000_i32, 2);
294 assert!(
295 decimal4_too_small.is_err(),
296 "Decimal4 precision underflow should fail"
297 );
298 assert!(decimal4_too_small
299 .unwrap_err()
300 .to_string()
301 .contains("wider than max precision"));
302
303 let decimal4_max_valid = VariantDecimal4::try_new(999_999_999_i32, 2);
305 assert!(
306 decimal4_max_valid.is_ok(),
307 "Decimal4 max valid value should succeed"
308 );
309
310 let decimal4_min_valid = VariantDecimal4::try_new(-999_999_999_i32, 2);
311 assert!(
312 decimal4_min_valid.is_ok(),
313 "Decimal4 min valid value should succeed"
314 );
315
316 let decimal8_too_large = VariantDecimal8::try_new(1_000_000_000_000_000_000_i64, 2);
318 assert!(
319 decimal8_too_large.is_err(),
320 "Decimal8 precision overflow should fail"
321 );
322 assert!(decimal8_too_large
323 .unwrap_err()
324 .to_string()
325 .contains("wider than max precision"));
326
327 let decimal8_too_small = VariantDecimal8::try_new(-1_000_000_000_000_000_000_i64, 2);
328 assert!(
329 decimal8_too_small.is_err(),
330 "Decimal8 precision underflow should fail"
331 );
332 assert!(decimal8_too_small
333 .unwrap_err()
334 .to_string()
335 .contains("wider than max precision"));
336
337 let decimal8_max_valid = VariantDecimal8::try_new(999_999_999_999_999_999_i64, 2);
339 assert!(
340 decimal8_max_valid.is_ok(),
341 "Decimal8 max valid value should succeed"
342 );
343
344 let decimal8_min_valid = VariantDecimal8::try_new(-999_999_999_999_999_999_i64, 2);
345 assert!(
346 decimal8_min_valid.is_ok(),
347 "Decimal8 min valid value should succeed"
348 );
349
350 let decimal16_too_large =
352 VariantDecimal16::try_new(100000000000000000000000000000000000000_i128, 2);
353 assert!(
354 decimal16_too_large.is_err(),
355 "Decimal16 precision overflow should fail"
356 );
357 assert!(decimal16_too_large
358 .unwrap_err()
359 .to_string()
360 .contains("wider than max precision"));
361
362 let decimal16_too_small =
363 VariantDecimal16::try_new(-100000000000000000000000000000000000000_i128, 2);
364 assert!(
365 decimal16_too_small.is_err(),
366 "Decimal16 precision underflow should fail"
367 );
368 assert!(decimal16_too_small
369 .unwrap_err()
370 .to_string()
371 .contains("wider than max precision"));
372
373 let decimal16_max_valid =
375 VariantDecimal16::try_new(99999999999999999999999999999999999999_i128, 2);
376 assert!(
377 decimal16_max_valid.is_ok(),
378 "Decimal16 max valid value should succeed"
379 );
380
381 let decimal16_min_valid =
382 VariantDecimal16::try_new(-99999999999999999999999999999999999999_i128, 2);
383 assert!(
384 decimal16_min_valid.is_ok(),
385 "Decimal16 min valid value should succeed"
386 );
387 }
388
389 #[test]
390 fn test_variant_decimal_invalid_scale() {
391 let decimal4_invalid_scale = VariantDecimal4::try_new(123_i32, 10);
393 assert!(
394 decimal4_invalid_scale.is_err(),
395 "Decimal4 with scale > 9 should fail"
396 );
397 assert!(decimal4_invalid_scale
398 .unwrap_err()
399 .to_string()
400 .contains("larger than max precision"));
401
402 let decimal4_invalid_scale_large = VariantDecimal4::try_new(123_i32, 20);
403 assert!(
404 decimal4_invalid_scale_large.is_err(),
405 "Decimal4 with scale > 9 should fail"
406 );
407
408 let decimal4_valid_scale = VariantDecimal4::try_new(123_i32, 9);
410 assert!(
411 decimal4_valid_scale.is_ok(),
412 "Decimal4 with scale = 9 should succeed"
413 );
414
415 let decimal8_invalid_scale = VariantDecimal8::try_new(123_i64, 19);
417 assert!(
418 decimal8_invalid_scale.is_err(),
419 "Decimal8 with scale > 18 should fail"
420 );
421 assert!(decimal8_invalid_scale
422 .unwrap_err()
423 .to_string()
424 .contains("larger than max precision"));
425
426 let decimal8_invalid_scale_large = VariantDecimal8::try_new(123_i64, 25);
427 assert!(
428 decimal8_invalid_scale_large.is_err(),
429 "Decimal8 with scale > 18 should fail"
430 );
431
432 let decimal8_valid_scale = VariantDecimal8::try_new(123_i64, 18);
434 assert!(
435 decimal8_valid_scale.is_ok(),
436 "Decimal8 with scale = 18 should succeed"
437 );
438
439 let decimal16_invalid_scale = VariantDecimal16::try_new(123_i128, 39);
441 assert!(
442 decimal16_invalid_scale.is_err(),
443 "Decimal16 with scale > 38 should fail"
444 );
445 assert!(decimal16_invalid_scale
446 .unwrap_err()
447 .to_string()
448 .contains("larger than max precision"));
449
450 let decimal16_invalid_scale_large = VariantDecimal16::try_new(123_i128, 50);
451 assert!(
452 decimal16_invalid_scale_large.is_err(),
453 "Decimal16 with scale > 38 should fail"
454 );
455
456 let decimal16_valid_scale = VariantDecimal16::try_new(123_i128, 38);
458 assert!(
459 decimal16_valid_scale.is_ok(),
460 "Decimal16 with scale = 38 should succeed"
461 );
462 }
463
464 #[test]
465 fn test_variant_decimal4_display() {
466 let d = VariantDecimal4::try_new(42, 0).unwrap();
468 assert_eq!(d.to_string(), "42");
469
470 let d = VariantDecimal4::try_new(-42, 0).unwrap();
471 assert_eq!(d.to_string(), "-42");
472
473 let d = VariantDecimal4::try_new(12345, 2).unwrap();
475 assert_eq!(d.to_string(), "123.45");
476
477 let d = VariantDecimal4::try_new(-12345, 2).unwrap();
478 assert_eq!(d.to_string(), "-123.45");
479
480 let d = VariantDecimal4::try_new(12300, 2).unwrap();
482 assert_eq!(d.to_string(), "123");
483
484 let d = VariantDecimal4::try_new(-12300, 2).unwrap();
485 assert_eq!(d.to_string(), "-123");
486
487 let d = VariantDecimal4::try_new(1005, 3).unwrap();
489 assert_eq!(d.to_string(), "1.005");
490
491 let d = VariantDecimal4::try_new(-1005, 3).unwrap();
492 assert_eq!(d.to_string(), "-1.005");
493
494 let d = VariantDecimal4::try_new(123, 4).unwrap();
496 assert_eq!(d.to_string(), "0.0123");
497
498 let d = VariantDecimal4::try_new(-123, 4).unwrap();
499 assert_eq!(d.to_string(), "-0.0123");
500
501 let d = VariantDecimal4::try_new(0, 0).unwrap();
503 assert_eq!(d.to_string(), "0");
504
505 let d = VariantDecimal4::try_new(0, 3).unwrap();
506 assert_eq!(d.to_string(), "0");
507
508 let d = VariantDecimal4::try_new(123456789, 9).unwrap();
510 assert_eq!(d.to_string(), "0.123456789");
511
512 let d = VariantDecimal4::try_new(-123456789, 9).unwrap();
513 assert_eq!(d.to_string(), "-0.123456789");
514
515 let d = VariantDecimal4::try_new(999999999, 0).unwrap();
517 assert_eq!(d.to_string(), "999999999");
518
519 let d = VariantDecimal4::try_new(-999999999, 0).unwrap();
520 assert_eq!(d.to_string(), "-999999999");
521
522 let d = VariantDecimal4::try_new(120050, 4).unwrap();
524 assert_eq!(d.to_string(), "12.005");
525
526 let d = VariantDecimal4::try_new(-120050, 4).unwrap();
527 assert_eq!(d.to_string(), "-12.005");
528 }
529
530 #[test]
531 fn test_variant_decimal8_display() {
532 let d = VariantDecimal8::try_new(42, 0).unwrap();
534 assert_eq!(d.to_string(), "42");
535
536 let d = VariantDecimal8::try_new(-42, 0).unwrap();
537 assert_eq!(d.to_string(), "-42");
538
539 let d = VariantDecimal8::try_new(1234567890, 3).unwrap();
541 assert_eq!(d.to_string(), "1234567.89");
542
543 let d = VariantDecimal8::try_new(-1234567890, 3).unwrap();
544 assert_eq!(d.to_string(), "-1234567.89");
545
546 let d = VariantDecimal8::try_new(123000000, 6).unwrap();
548 assert_eq!(d.to_string(), "123");
549
550 let d = VariantDecimal8::try_new(-123000000, 6).unwrap();
551 assert_eq!(d.to_string(), "-123");
552
553 let d = VariantDecimal8::try_new(100005, 6).unwrap();
555 assert_eq!(d.to_string(), "0.100005");
556
557 let d = VariantDecimal8::try_new(-100005, 6).unwrap();
558 assert_eq!(d.to_string(), "-0.100005");
559
560 let d = VariantDecimal8::try_new(123, 10).unwrap();
562 assert_eq!(d.to_string(), "0.0000000123");
563
564 let d = VariantDecimal8::try_new(-123, 10).unwrap();
565 assert_eq!(d.to_string(), "-0.0000000123");
566
567 let d = VariantDecimal8::try_new(0, 0).unwrap();
569 assert_eq!(d.to_string(), "0");
570
571 let d = VariantDecimal8::try_new(0, 10).unwrap();
572 assert_eq!(d.to_string(), "0");
573
574 let d = VariantDecimal8::try_new(123456789012345678, 18).unwrap();
576 assert_eq!(d.to_string(), "0.123456789012345678");
577
578 let d = VariantDecimal8::try_new(-123456789012345678, 18).unwrap();
579 assert_eq!(d.to_string(), "-0.123456789012345678");
580
581 let d = VariantDecimal8::try_new(999999999999999999, 0).unwrap();
583 assert_eq!(d.to_string(), "999999999999999999");
584
585 let d = VariantDecimal8::try_new(-999999999999999999, 0).unwrap();
586 assert_eq!(d.to_string(), "-999999999999999999");
587
588 let d = VariantDecimal8::try_new(1200000050000, 10).unwrap();
590 assert_eq!(d.to_string(), "120.000005");
591
592 let d = VariantDecimal8::try_new(-1200000050000, 10).unwrap();
593 assert_eq!(d.to_string(), "-120.000005");
594 }
595
596 #[test]
597 fn test_variant_decimal16_display() {
598 let d = VariantDecimal16::try_new(42, 0).unwrap();
600 assert_eq!(d.to_string(), "42");
601
602 let d = VariantDecimal16::try_new(-42, 0).unwrap();
603 assert_eq!(d.to_string(), "-42");
604
605 let d = VariantDecimal16::try_new(123456789012345, 4).unwrap();
607 assert_eq!(d.to_string(), "12345678901.2345");
608
609 let d = VariantDecimal16::try_new(-123456789012345, 4).unwrap();
610 assert_eq!(d.to_string(), "-12345678901.2345");
611
612 let d = VariantDecimal16::try_new(12300000000, 8).unwrap();
614 assert_eq!(d.to_string(), "123");
615
616 let d = VariantDecimal16::try_new(-12300000000, 8).unwrap();
617 assert_eq!(d.to_string(), "-123");
618
619 let d = VariantDecimal16::try_new(10000005, 8).unwrap();
621 assert_eq!(d.to_string(), "0.10000005");
622
623 let d = VariantDecimal16::try_new(-10000005, 8).unwrap();
624 assert_eq!(d.to_string(), "-0.10000005");
625
626 let d = VariantDecimal16::try_new(123, 20).unwrap();
628 assert_eq!(d.to_string(), "0.00000000000000000123");
629
630 let d = VariantDecimal16::try_new(-123, 20).unwrap();
631 assert_eq!(d.to_string(), "-0.00000000000000000123");
632
633 let d = VariantDecimal16::try_new(0, 0).unwrap();
635 assert_eq!(d.to_string(), "0");
636
637 let d = VariantDecimal16::try_new(0, 20).unwrap();
638 assert_eq!(d.to_string(), "0");
639
640 let d = VariantDecimal16::try_new(12345678901234567890123456789012345678_i128, 38).unwrap();
642 assert_eq!(d.to_string(), "0.12345678901234567890123456789012345678");
643
644 let d =
645 VariantDecimal16::try_new(-12345678901234567890123456789012345678_i128, 38).unwrap();
646 assert_eq!(d.to_string(), "-0.12345678901234567890123456789012345678");
647
648 let d = VariantDecimal16::try_new(99999999999999999999999999999999999999_i128, 0).unwrap();
650 assert_eq!(d.to_string(), "99999999999999999999999999999999999999");
651
652 let d = VariantDecimal16::try_new(-99999999999999999999999999999999999999_i128, 0).unwrap();
653 assert_eq!(d.to_string(), "-99999999999999999999999999999999999999");
654
655 let d = VariantDecimal16::try_new(12000000000000050000000000000_i128, 25).unwrap();
657 assert_eq!(d.to_string(), "1200.000000000005");
658
659 let d = VariantDecimal16::try_new(-12000000000000050000000000000_i128, 25).unwrap();
660 assert_eq!(d.to_string(), "-1200.000000000005");
661
662 let large_int = 12345678901234567890123456789_i128;
664 let d = VariantDecimal16::try_new(large_int, 0).unwrap();
665 assert_eq!(d.to_string(), "12345678901234567890123456789");
666
667 let d = VariantDecimal16::try_new(-large_int, 0).unwrap();
668 assert_eq!(d.to_string(), "-12345678901234567890123456789");
669 }
670}