1use std::cmp::Ordering;
4
5use crate::ion_data::{IonDataHash, IonDataOrd, IonEq};
6use crate::result::{IonError, IonFailure};
7use crate::types::integer::{IntData, UIntData};
8use crate::{Int, IonResult};
9use num_bigint::{BigInt, BigUint};
10use num_traits::Zero;
11use std::convert::{TryFrom, TryInto};
12use std::fmt::{Display, Formatter};
13use std::hash::{Hash, Hasher};
14use std::ops::Neg;
15
16pub(crate) mod coefficient;
17pub use coefficient::{Coefficient, Sign};
18
19#[derive(Clone, Debug)]
43pub struct Decimal {
44 pub(crate) coefficient_value: Int,
80 pub(crate) coefficient_sign: Sign,
81 pub(crate) exponent: i64,
82}
83
84impl Decimal {
85 pub const ZERO: Decimal = Decimal {
86 coefficient_value: Int::ZERO,
87 coefficient_sign: Sign::Positive,
88 exponent: 0,
89 };
90
91 pub const NEGATIVE_ZERO: Decimal = Decimal {
92 coefficient_value: Int::ZERO,
93 coefficient_sign: Sign::Negative,
94 exponent: 0,
95 };
96
97 pub fn new<C: Into<Coefficient>, E: Into<i64>>(coefficient: C, exponent: E) -> Decimal {
100 let coefficient = coefficient.into();
101 let exponent = exponent.into();
102 if coefficient.is_negative_zero() {
103 return Decimal::negative_zero_with_exponent(exponent);
104 }
105 Decimal {
106 coefficient_value: coefficient.as_int().unwrap(), coefficient_sign: coefficient.sign(),
108 exponent,
109 }
110 }
111
112 pub fn coefficient(&self) -> Coefficient {
114 Coefficient::from_sign_and_value(self.coefficient_sign, self.coefficient_value.clone())
115 }
116
117 pub fn exponent(&self) -> i64 {
119 self.exponent
120 }
121
122 pub fn scale(&self) -> i64 {
127 self.exponent.neg()
128 }
129
130 pub fn precision(&self) -> u64 {
132 self.coefficient().number_of_decimal_digits() as u64
133 }
134
135 pub fn negative_zero() -> Decimal {
138 Decimal::negative_zero_with_exponent(0)
139 }
140
141 pub fn negative_zero_with_exponent(exponent: i64) -> Decimal {
145 Decimal {
146 coefficient_value: Int::ZERO,
147 coefficient_sign: Sign::Negative,
148 exponent,
149 }
150 }
151
152 pub fn is_zero(&self) -> bool {
154 self.coefficient().is_zero()
155 }
156
157 pub fn is_less_than_zero(&self) -> bool {
160 let coefficient = self.coefficient();
161 coefficient.sign() == Sign::Negative && !coefficient.magnitude().is_zero()
162 }
163
164 pub(crate) fn is_greater_than_or_equal_to_one(&self) -> bool {
166 if self.coefficient().is_zero() {
169 return false;
170 }
171
172 if self.exponent >= 0 {
175 return true;
176 }
177
178 let num_coefficient_decimal_digits = self.coefficient().number_of_decimal_digits() as u64;
181 num_coefficient_decimal_digits > self.exponent.unsigned_abs()
182 }
183
184 fn compare(d1: &Decimal, d2: &Decimal) -> Ordering {
187 if d1.is_zero() && d2.is_zero() {
188 return Ordering::Equal;
190 }
191 let sign_cmp = d1.coefficient().sign().cmp(&d2.coefficient().sign());
194 if sign_cmp != Ordering::Equal {
195 return sign_cmp;
196 }
197
198 let ordering = Decimal::compare_magnitudes(d1, d2);
200
201 if d1.coefficient().sign() == Sign::Positive {
202 ordering
204 } else {
205 ordering.reverse()
209 }
210 }
211
212 fn compare_magnitudes(d1: &Decimal, d2: &Decimal) -> Ordering {
214 if d1.exponent == d2.exponent {
216 return d1
217 .coefficient()
218 .magnitude()
219 .cmp(&d2.coefficient().magnitude());
220 }
221
222 if d1.exponent > d2.exponent {
228 Self::compare_scaled_coefficients(d1, d2)
229 } else {
230 Self::compare_scaled_coefficients(d2, d1).reverse()
231 }
232 }
233
234 fn compare_scaled_coefficients(d1: &Decimal, d2: &Decimal) -> Ordering {
237 let exponent_delta = d1.exponent - d2.exponent;
238 let d1_mag = d1.coefficient().magnitude();
246 let d2_mag = d2.coefficient().magnitude();
247 if let (Some(m1), Some(m2)) = (d1_mag.as_u128(), d2_mag.as_u128()) {
249 if let Some(multiplicand) = 10u128.checked_pow(exponent_delta as u32) {
250 if let Some(scaled) = m1.checked_mul(multiplicand) {
251 return scaled.cmp(&m2);
252 }
253 }
254 }
255 let d1_big = d1_mag.data;
257 let scaled = d1_big * UIntData::from_big(BigUint::from(10u32).pow(exponent_delta as u32));
258 let other = d2_mag.data;
259 scaled.cmp(&other)
260 }
261
262 pub fn trunc(&self) -> Decimal {
265 if self.exponent >= 0 {
266 self.clone()
267 } else {
268 let mut coeff = self.coefficient().as_int().unwrap_or(Int::ZERO).data;
271 let scaling_factor =
272 IntData::from_big(BigInt::from(10).pow(self.exponent.unsigned_abs() as u32));
273 coeff = coeff / scaling_factor;
274 Decimal::new(
275 Coefficient::from_sign_and_value(self.coefficient_sign, coeff),
276 0,
277 )
278 }
279 }
280
281 pub fn fract(&self) -> Decimal {
284 if self.exponent >= 0 {
285 Decimal::new(
286 Coefficient::from_sign_and_value(self.coefficient_sign, 0),
287 0,
288 )
289 } else {
290 let mut coeff = self.coefficient().as_int().unwrap_or(Int::ZERO).data;
291 let scaling_factor =
292 IntData::from_big(BigInt::from(10).pow(self.exponent.unsigned_abs() as u32));
293 coeff = coeff % scaling_factor;
294 Decimal::new(
295 Coefficient::from_sign_and_value(self.coefficient_sign, coeff),
296 self.exponent,
297 )
298 }
299 }
300}
301
302impl PartialEq for Decimal {
303 fn eq(&self, other: &Self) -> bool {
304 self.cmp(other) == Ordering::Equal
305 }
306}
307
308impl Eq for Decimal {}
309
310impl IonEq for Decimal {
311 fn ion_eq(&self, other: &Self) -> bool {
312 self.exponent == other.exponent && self.coefficient() == other.coefficient()
313 }
314}
315
316impl IonDataOrd for Decimal {
317 fn ion_cmp(&self, other: &Self) -> Ordering {
319 let sign_cmp = self.coefficient().sign().cmp(&other.coefficient().sign());
320 if sign_cmp != Ordering::Equal {
321 return sign_cmp;
322 }
323
324 let ordering = Decimal::compare_magnitudes(self, other);
326 if ordering != Ordering::Equal {
327 return match self.coefficient().sign() {
328 Sign::Negative => ordering.reverse(),
329 Sign::Positive => ordering,
330 };
331 };
332 self.exponent.cmp(&other.exponent).reverse()
335 }
336}
337
338impl IonDataHash for Decimal {
339 fn ion_data_hash<H: Hasher>(&self, state: &mut H) {
340 state.write_i8(self.coefficient().sign() as i8);
341 self.coefficient().magnitude().hash(state);
342 state.write_i64(self.exponent);
343 }
344}
345
346impl PartialOrd for Decimal {
347 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
348 Some(self.cmp(other))
349 }
350}
351
352impl Ord for Decimal {
353 fn cmp(&self, other: &Self) -> Ordering {
354 Decimal::compare(self, other)
355 }
356}
357
358macro_rules! impl_decimal_from_unsigned_primitive_integer {
359 ($($t:ty),*) => ($(
360 impl From<$t> for Decimal {
361 fn from(value: $t) -> Self {
362 Decimal::new(value as u64, 0)
363 }
364 }
365 )*)
366}
367impl_decimal_from_unsigned_primitive_integer!(u8, u16, u32, u64, usize);
368
369macro_rules! impl_decimal_from_signed_primitive_integer {
370 ($($t:ty),*) => ($(
371 impl From<$t> for Decimal {
372 fn from(value: $t) -> Self {
373 Decimal::new(Coefficient::new(value), 0)
374 }
375 }
376 )*)
377}
378impl_decimal_from_signed_primitive_integer!(i8, i16, i32, i64, isize);
379
380impl From<Int> for Decimal {
381 fn from(value: Int) -> Self {
382 Decimal::new(value, 0)
383 }
384}
385
386impl TryFrom<f32> for Decimal {
387 type Error = IonError;
388
389 fn try_from(value: f32) -> Result<Self, Self::Error> {
390 (value as f64).try_into()
392 }
393}
394
395impl TryFrom<f64> for Decimal {
396 type Error = IonError;
397 fn try_from(value: f64) -> Result<Self, Self::Error> {
413 if value.is_infinite() {
416 if value.is_sign_negative() {
417 return IonResult::illegal_operation(
418 "Cannot convert f64 negative infinity to Decimal.",
419 );
420 } else {
421 return IonResult::illegal_operation("Cannot convert f64 infinity to Decimal.");
422 }
423 } else if value.is_nan() {
424 return IonResult::illegal_operation(
425 "Cannot convert f64 NaN (not-a-number) to Decimal.",
426 );
427 }
428
429 if value == 0f64 {
433 if value.is_sign_negative() {
436 return Ok(Decimal::NEGATIVE_ZERO);
437 }
438 return Ok(Decimal::ZERO);
439 }
440
441 let f64_int = value.trunc();
445 let f64_fract = value.fract();
451 const MAX_DECIMAL_DIGITS: u32 = 38;
465 let integral_value = f64_int as i128;
482 let num_integral_decimal_digits = f64_int.abs().log10().floor() as u32 + 1;
484
485 if f64_fract.is_zero() {
487 let exponent = (num_integral_decimal_digits as i64 - MAX_DECIMAL_DIGITS as i64).max(0);
491 return Ok(Decimal::new(integral_value, exponent));
492 }
493
494 let num_fractional_digits =
499 (MAX_DECIMAL_DIGITS - num_integral_decimal_digits).min(f64::DIGITS);
500 let coefficient_f64 = value * 10f64.powi(num_fractional_digits as i32);
505 let coefficient = coefficient_f64 as i128;
506
507 let exponent = -(num_fractional_digits as i64);
508 Ok(Decimal::new(coefficient, exponent))
509 }
510}
511
512impl Display for Decimal {
513 #[rustfmt::skip] fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
515 const WIDE_NUMBER: usize = 6; let digits = &*self.coefficient().magnitude().to_string();
519 let len = digits.len();
520 let dot_index = len as i64 + self.exponent;
524
525 if self.coefficient().sign() == Sign::Negative {
526 write!(f, "-").unwrap();
527 };
528
529 if self.exponent == 0 && len > WIDE_NUMBER { write!(f, "{}.{}d{}", &digits[0..1], &digits[1..len], (dot_index - 1))
531 } else if self.exponent == 0 { write!(f, "{}.", &digits)
533 } else if self.exponent >= 0 { write!(f, "{}d{}", &digits, self.exponent)
535 } else { if dot_index > 0 { let dot_index = dot_index as usize;
538 write!(f, "{}.{}", &digits[0..dot_index], &digits[dot_index..len])
539 } else if dot_index > -(WIDE_NUMBER as i64) { let width = dot_index.unsigned_abs() as usize + len;
541 write!(f, "0.{digits:0>width$}")
542 } else { write!(f, "{}.{}d{}", &digits[0..1], &digits[1..len], (dot_index - 1))
544 }
545 }
546 }
547}
548
549#[cfg(feature = "bigdecimal")]
550mod bigdecimal {
551 use crate::result::IonFailure;
552 use crate::{Decimal, IonError, IonResult};
553 use bigdecimal::BigDecimal;
554 use num_traits::ToPrimitive;
555
556 impl TryInto<BigDecimal> for Decimal {
557 type Error = IonError;
558
559 fn try_into(self) -> Result<BigDecimal, Self::Error> {
562 if self.coefficient().is_negative_zero() {
563 return IonResult::illegal_operation("Cannot convert negative zero to BigDecimal.");
564 }
565 let bigint = self.coefficient_value.to_bigint();
566 Ok(BigDecimal::new(bigint, self.scale()))
567 }
568 }
569
570 impl TryFrom<BigDecimal> for Decimal {
571 type Error = IonError;
572
573 fn try_from(value: BigDecimal) -> Result<Self, Self::Error> {
576 let (coeff, exponent) = value.into_bigint_and_exponent();
577 let Some(data) = coeff.to_i128() else {
578 return IonResult::illegal_operation("Cannot represent coefficient as i128.");
579 };
580
581 Ok(Decimal::new(data, -exponent))
582 }
583 }
584
585 #[cfg(test)]
586 mod tests {
587 use crate::Decimal;
588 use bigdecimal::BigDecimal;
589 use rstest::*;
590
591 #[fixture]
592 fn no_such_decimal() -> Decimal {
596 Decimal::NEGATIVE_ZERO
597 }
598
599 #[rstest]
600 #[case("123e1", Decimal::new(123, 1))]
601 #[case("123.", Decimal::new(123, 0))]
602 #[case("-123.", Decimal::new(-123, 0))]
603 #[case("12.3", Decimal::new( 123, -1))]
604 #[case("0.123", Decimal::new( 123, -3))]
605 #[case("-0.00123", Decimal::new(-123, -5))]
606 #[case("0.00123", Decimal::new( 123, -5))]
607 #[case("1.23e-8", Decimal::new( 123, -10))]
608 #[case("-1.23e-8", Decimal::new(-123, -10))]
609 #[case::out_of_double("9_007_199_254_740_993", Decimal::new(9_007_199_254_740_993i128, 0))]
610 #[should_panic]
611 #[case::coeff_too_large(
612 "1427247692705959881058285969449495136382746624",
613 no_such_decimal()
614 )]
615 fn try_from_bigdecimal_for_decimal(#[case] input: BigDecimal, #[case] expected: Decimal) {
618 let actual = Decimal::try_from(input).unwrap();
619 assert_eq!(actual, expected);
620 }
621
622 #[fixture]
623 fn no_such_bigdecimal() -> BigDecimal {
627 0.into()
628 }
629
630 #[rstest]
631 #[case(Decimal::new(123, 1), "123e1")]
632 #[case(Decimal::new(123, 0), "123.")]
633 #[case(Decimal::new(-123, 0),"-123.")]
634 #[case(Decimal::new( 123, -1), "12.3")]
635 #[case(Decimal::new( 123, -3), "0.123")]
636 #[case(Decimal::new(-123, -5), "-0.00123")]
637 #[case(Decimal::new( 123, -5), "0.00123")]
638 #[case(Decimal::new( 123, -10), "1.23e-8")]
639 #[case(Decimal::new(-123, -10), "-1.23e-8")]
640 #[should_panic]
641 #[case::negative_zero(Decimal::NEGATIVE_ZERO, no_such_bigdecimal())]
642 fn try_into_bigdecimal_for_decimal(#[case] input: Decimal, #[case] expected: BigDecimal) {
645 let actual: BigDecimal = Decimal::try_into(input).unwrap();
646 assert_eq!(actual, expected);
647 }
648
649 #[test]
650 fn convert_large_coefficient_decimal_to_bigdecimal() {
651 use crate::decimal::Coefficient;
652 use crate::Int;
653 let mut bytes = vec![1u8];
655 bytes.extend(vec![0u8; 16]);
656 bytes[16] = 1;
657 let big_int = Int::from_le_signed_bytes(&bytes);
658 let d = Decimal::new(Coefficient::new(big_int), 0i64);
659 let result: Result<BigDecimal, _> = d.try_into();
660 assert!(result.is_ok());
661 }
662 }
663}
664
665#[cfg(test)]
666mod decimal_tests {
667 use crate::decimal::{Coefficient, Sign};
668 use crate::result::IonResult;
669 use crate::{Decimal, Int};
670
671 use num_traits::Float;
672 use std::cmp::Ordering;
673 use std::convert::TryInto;
674 use std::fmt::Write;
675
676 use crate::ion_data::IonEq;
677
678 use rstest::*;
679
680 #[rstest]
681 #[case(Decimal::new(123, 1), "123d1")]
682 #[case(Decimal::new(123, 0), "123.")]
683 #[case(Decimal::new(-123, 0),"-123.")]
684 #[case(Decimal::new( 123, -1), "12.3")]
685 #[case(Decimal::new( 123, -3), "0.123")]
686 #[case(Decimal::new(-123, -5), "-0.00123")]
687 #[case(Decimal::new( 123, -5), "0.00123")]
688 #[case(Decimal::new( 123, -10), "1.23d-8")]
689 #[case(Decimal::new(-123, -10), "-1.23d-8")]
690 fn test_display(#[case] decimal: Decimal, #[case] expected: &str) {
691 let mut buffer = String::new();
692 write!(buffer, "{decimal}").unwrap();
693 assert_eq!(buffer.as_str(), expected);
694 }
695
696 #[test]
697 fn test_decimal_eq_negative_zeros() {
698 assert_eq!(Decimal::negative_zero(), Decimal::negative_zero());
700 assert_eq!(
701 Decimal::negative_zero_with_exponent(2),
702 Decimal::negative_zero_with_exponent(7)
703 );
704 assert_eq!(
705 Decimal::new(0, 0),
706 Decimal::new(Coefficient::negative_zero(), 0)
707 );
708 }
709
710 #[test]
711 fn test_decimal_ion_eq_negative_zeros() {
712 assert!(Decimal::negative_zero().ion_eq(&Decimal::negative_zero()));
714 assert!(!Decimal::negative_zero_with_exponent(2)
715 .ion_eq(&Decimal::negative_zero_with_exponent(7)));
716 assert!(!Decimal::new(0, 0).ion_eq(&Decimal::new(Coefficient::negative_zero(), 0)));
717 }
718
719 #[rstest]
720 #[case((80, 2), (80, 2), true)]
723 #[case((124, -2), (1240, -3), true)]
724 #[case((0, 0), (0, 0), true)]
725 #[case((0, -2), (0, 3), true)]
726 #[case((0, 2), (0, 5), true)]
727 fn test_decimal_eq<I: Into<Coefficient>>(
728 #[case] components1: (I, i64),
729 #[case] components2: (I, i64),
730 #[case] is_equal: bool,
731 ) {
732 let decimal1 = Decimal::new(components1.0.into(), components1.1);
733 let decimal2 = Decimal::new(components2.0.into(), components2.1);
734 assert_eq!(decimal1 == decimal2, is_equal);
735 }
736
737 #[rstest]
738 #[case((80, 2), (80, 2), true)]
741 #[case((124, -2), (124, -2), true)]
742 #[case((-124, -2), (-124, -2), true)]
743 #[case((124, -2), (1240, -3), false)]
744 #[case((0, 0), (0, 0), true)]
745 #[case((0, -2), (0, -3), false)]
746 #[case((0, -2), (0, 3), false)]
747 #[case((0, -2), (0, -2), true)]
748 #[case((0, 2), (0, 5), false)]
749 fn test_decimal_ion_eq<I: Into<Coefficient>>(
750 #[case] components1: (I, i64),
751 #[case] components2: (I, i64),
752 #[case] ion_eq_expected: bool,
753 ) {
754 let decimal1 = Decimal::new(components1.0.into(), components1.1);
755 let decimal2 = Decimal::new(components2.0.into(), components2.1);
756 assert_eq!(decimal1.ion_eq(&decimal2), ion_eq_expected);
757 }
758
759 #[rstest]
760 #[case((80, 3), Ordering::Equal, (80, 3))]
763 #[case((80, 3), Ordering::Greater, (79, 3))]
764 #[case((80, 3), Ordering::Less, (81, 3))]
765 #[case((80, 3), Ordering::Greater, (80, 2))]
766 #[case((80, 3), Ordering::Less, (80, 4))]
767 #[case((80, 3), Ordering::Equal, (8, 4))]
768 #[case((80, 3), Ordering::Equal, (800, 2))]
769 #[case((-80, 3), Ordering::Equal, (-80, 3))]
771 #[case((-80, 3), Ordering::Less, (-79, 3))]
772 #[case((-80, 3), Ordering::Greater, (-81, 3))]
773 #[case((-80, 3), Ordering::Less, (-80, 2))]
774 #[case((-80, 3), Ordering::Greater, (-80, 4))]
775 #[case((-80, 3), Ordering::Equal, (-8, 4))]
776 #[case((-80, 3), Ordering::Equal, (-800, 2))]
777 #[case((0, 3), Ordering::Equal, (0, 3))]
779 #[case((0, 3), Ordering::Greater, (-1, 3))]
780 #[case((0, 3), Ordering::Less, (1, 3))]
781 #[case((0, 3), Ordering::Equal, (0, -2))]
782 #[case((0, 3), Ordering::Equal, (0, -1))]
783 #[case((0, 3), Ordering::Equal, (0, 0))]
784 #[case((0, 3), Ordering::Equal, (0, 1))]
785 #[case((0, 3), Ordering::Equal, (0, 2))]
786 #[case((0, 3), Ordering::Equal, (Coefficient::NEGATIVE_ZERO, -1))]
788 #[case((0, 3), Ordering::Equal, (Coefficient::NEGATIVE_ZERO, 0))]
789 #[case((0, 3), Ordering::Equal, (Coefficient::NEGATIVE_ZERO, 1))]
790 #[case((Coefficient::NEGATIVE_ZERO, 3), Ordering::Equal, (Coefficient::NEGATIVE_ZERO, -1))]
791 #[case((Coefficient::NEGATIVE_ZERO, 3), Ordering::Equal, (Coefficient::NEGATIVE_ZERO, 0))]
792 #[case((Coefficient::NEGATIVE_ZERO, 3), Ordering::Equal, (Coefficient::NEGATIVE_ZERO, 1))]
793 #[case((-1000, -1), Ordering::Less, (-99_999_999_999i64, -9))]
795 #[case((1000, -1), Ordering::Greater, (99_999_999_999i64, -9))]
796 fn test_decimal_ord<A: Into<Coefficient>, B: Into<Coefficient>>(
797 #[case] components1: (A, i64),
798 #[case] ordering: Ordering,
799 #[case] components2: (B, i64),
800 ) {
801 let decimal1 = Decimal::new(components1.0.into(), components1.1);
802 let decimal2 = Decimal::new(components2.0.into(), components2.1);
803 assert_eq!(decimal1.cmp(&decimal2), ordering);
804 assert_eq!(decimal2.cmp(&decimal1), ordering.reverse());
806 }
807
808 #[rstest]
809 #[case(i32::MIN as f64, Decimal::from(i32::MIN))]
811 #[case(10.0, Decimal::from(10))]
812 #[case(1.0, Decimal::from(1))]
813 #[case(0.0, Decimal::ZERO)]
814 #[case((2i64.pow(53) - 1) as f64, Decimal::new(2i64.pow(53) - 1, 0))]
816 #[case(-0.0, Decimal::NEGATIVE_ZERO)]
818 #[case(-1.0, Decimal::from(-1))]
819 #[case(-10.0, Decimal::from(-10))]
820 #[case(i32::MAX as f64, Decimal::from(i32::MAX))]
821 #[case((-(2i64.pow(53)) + 1) as f64, Decimal::new(-(2i64.pow(53)) + 1, 0))]
823 #[case(8.67, Decimal::new(867, -2))]
825 #[case(8.6753, Decimal::new(86753, -4))]
826 #[case(8.675309, Decimal::new(8675309, -6))]
827 #[case(-8.67, Decimal::new(-867, -2))]
829 #[case(-8.6753, Decimal::new(-86753, -4))]
830 #[case(-8.675309, Decimal::new(-8675309, -6))]
831 #[case(0.2, Decimal::new(2, -1))]
833 #[case(0.24, Decimal::new(24, -2))]
834 #[case(0.246, Decimal::new(246, -3))]
835 #[case(0.24601, Decimal::new(24601, -5))]
836 #[case(-0.2, Decimal::new(-2, -1))]
838 #[case(-0.24, Decimal::new(-24, -2))]
839 #[case(-0.246, Decimal::new(-246, -3))]
840 #[case(-0.24601, Decimal::new(-24601, -5))]
841 #[case(0.000_000_000_000_001, Decimal::new(1, -15))]
843 #[case(-0.000_000_000_000_001, Decimal::new(-1, -15))]
844 fn test_decimal_try_from_f64_ok(#[case] value: f64, #[case] expected: Decimal) {
845 let actual: Decimal = value.try_into().unwrap();
846 assert_eq!(
847 actual, expected,
848 "float {value}: actual {actual} != expected {expected}"
849 );
850 }
851
852 #[rstest]
853 #[case::positive_infinity(f64::infinity())]
854 #[case::negative_infinity(f64::neg_infinity())]
855 #[case::nan(f64::nan())]
856 fn test_decimal_try_from_f64_err(#[case] value: f64) {
857 let conversion_result: IonResult<Decimal> = value.try_into();
858 assert!(conversion_result.is_err());
859 }
860
861 #[rstest]
862 #[case(Decimal::new(23, -3), 3)]
863 #[case(Decimal::new(23, -2), 2)]
864 #[case(Decimal::new(23, -1), 1)]
865 #[case(Decimal::new(23, 0), 0)]
866 #[case(Decimal::new(23, 1), -1)]
867 #[case(Decimal::new(23, 2), -2)]
868 #[case(Decimal::new(23, 3), -3)]
869 #[case(Decimal::new(4, 3), -3)]
870 #[case(Decimal::new(40, 3), -3)]
871 #[case(Decimal::new(400, 3), -3)]
872 #[case(Decimal::new(5, -4), 4)]
873 #[case(Decimal::new(50, -4), 4)]
874 #[case(Decimal::new(500, -4), 4)]
875 #[case(Decimal::new(0, 0), 0)]
876 #[case(Decimal::negative_zero(), 0)]
877 #[case(Decimal::negative_zero_with_exponent(1), -1)]
878 #[case(Decimal::negative_zero_with_exponent(2), -2)]
879 #[case(Decimal::new(u64::MAX, -5), 5)]
880 #[case(Decimal::new(u64::MAX, 0), 0)]
881 fn test_scale(#[case] value: Decimal, #[case] expected: i64) {
882 assert_eq!(value.scale(), expected)
883 }
884
885 #[rstest]
886 #[case(Decimal::new(-24600, -3), 5)]
887 #[case(Decimal::new(-24600, -2), 5)]
888 #[case(Decimal::new(-24600, -1), 5)]
889 #[case(Decimal::new(-24600, 0), 5)]
890 #[case(Decimal::new(-24600, 1), 5)]
891 #[case(Decimal::new(-24600, 2), 5)]
892 #[case(Decimal::new(-24600, 3), 5)]
893 #[case(Decimal::new(5, -3), 1)]
894 #[case(Decimal::new(50, -3), 2)]
895 #[case(Decimal::new(500, -3), 3)]
896 #[case(Decimal::new(6, 3), 1)]
897 #[case(Decimal::new(60, 3), 2)]
898 #[case(Decimal::new(600, 3), 3)]
899 #[case(Decimal::new(0, -2), 1)]
900 #[case(Decimal::new(0, -1), 1)]
901 #[case(Decimal::new(0, 0), 1)]
902 #[case(Decimal::new(0, 1), 1)]
903 #[case(Decimal::new(0, 2), 1)]
904 #[case(Decimal::negative_zero_with_exponent(-2), 1)]
905 #[case(Decimal::negative_zero_with_exponent(-1), 1)]
906 #[case(Decimal::negative_zero(), 1)]
907 #[case(Decimal::negative_zero_with_exponent(1), 1)]
908 #[case(Decimal::negative_zero_with_exponent(2), 1)]
909 #[case(Decimal::new(u64::MAX, 3), 20)]
910 #[case(Decimal::new(i128::MAX, -2), 39)]
911 fn test_precision(#[case] value: Decimal, #[case] expected: u64) {
912 assert_eq!(value.precision(), expected);
913 }
914
915 #[rstest]
916 #[case(0, Decimal::new(0, 0))]
917 #[case(1, Decimal::new(1, 0))]
918 #[case(-1, Decimal::new(-1, 0))]
919 #[case(-8675309i64, Decimal::new(-8675309i64, 0))]
920 #[case(8675309u32, Decimal::new(8675309u32, 0))]
921 #[case(8675309i64, Decimal::new(8675309u32, 0))]
923 #[case(Int::from(-8675309i64), Decimal::new(-8675309i64, 0))]
924 #[case(Int::from(-8675309i128), Decimal::new(-8675309i64, 0))]
925 fn decimal_from_integers(
926 #[case] coefficient: impl Into<Coefficient>,
927 #[case] expected: Decimal,
928 ) {
929 assert_eq!(Decimal::new(coefficient, 0), expected);
930 }
931
932 #[rstest]
933 #[case(Decimal::new(1, 0), Decimal::new(1, 0))]
934 #[case(Decimal::new(15, -1), Decimal::new(1, 0))]
935 #[case(Decimal::new(105, -1), Decimal::new(10, 0))]
936 #[case(Decimal::new(-5, -1), Decimal::new(0, 0))]
937 #[case(Decimal::new(-5, -1), Decimal::NEGATIVE_ZERO)]
938 #[case(Decimal::NEGATIVE_ZERO, Decimal::NEGATIVE_ZERO)]
939 #[case(Decimal::new(0, -5), Decimal::new(0, 0))]
940 #[case(Decimal::new(Coefficient::from_sign_and_value(Sign::Negative, 0), -5), Decimal::new(0, 0))]
941 fn decimal_trunc(#[case] value: Decimal, #[case] expected: Decimal) {
942 assert_eq!(value.trunc(), expected);
943 }
944
945 #[rstest]
946 #[case(Decimal::new(1, 0), Decimal::new(0, 0))]
947 #[case(Decimal::new(15, -1), Decimal::new(5, -1))]
948 #[case(Decimal::new(105, -1), Decimal::new(5, -1))]
949 fn decimal_fract(#[case] value: Decimal, #[case] expected: Decimal) {
950 assert_eq!(value.fract(), expected);
951 }
952
953 #[test]
954 fn decimal_cmp_arbitrary_precision() {
955 use crate::ion_data::IonDataHash;
956 use std::collections::hash_map::DefaultHasher;
957 use std::hash::Hasher;
958
959 let mut bytes = vec![0u8; 18];
961 bytes[0] = 42;
962 bytes[16] = 1;
963 let big_value = Int::from_le_signed_bytes(&bytes);
964
965 let mut bytes2 = vec![0u8; 18];
966 bytes2[16] = 2;
967 let bigger_value = Int::from_le_signed_bytes(&bytes2);
968
969 let d1 = Decimal::new(Coefficient::new(big_value.clone()), 0i64);
970 let d2 = Decimal::new(Coefficient::new(big_value), 0i64);
971 let small = Decimal::new(1i64, 0i64);
972 let zero = Decimal::new(0i64, 0i64);
973
974 assert_eq!(d1, d2);
976
977 assert_eq!(d1.cmp(&small), Ordering::Greater);
979 assert_eq!(small.cmp(&d1), Ordering::Less);
980
981 assert_eq!(d1.cmp(&zero), Ordering::Greater);
983
984 assert!(!d1.is_zero());
986 assert!(zero.is_zero());
987
988 let hash = |d: &Decimal| {
990 let mut h = DefaultHasher::new();
991 d.ion_data_hash(&mut h);
992 h.finish()
993 };
994 assert_eq!(hash(&d1), hash(&d2));
995
996 let d3 = Decimal::new(Coefficient::new(bigger_value), 0i64);
998 assert_eq!(d1.cmp(&d3), Ordering::Less);
999 assert_eq!(d3.cmp(&d1), Ordering::Greater);
1000 }
1001
1002 #[test]
1003 fn compare_decimals_with_large_exponent_difference() {
1004 let d1 = Decimal::new(1i64, 40i64);
1007 let d2 = Decimal::new(1i64, 0i64);
1008 assert_eq!(d1.cmp(&d2), Ordering::Greater);
1009 }
1010
1011 #[test]
1012 fn trunc_decimal_with_large_negative_exponent() {
1013 let d = Decimal::new(1i64, -40i64);
1015 assert_eq!(d.trunc(), Decimal::new(0i64, 0i64));
1016 }
1017
1018 #[test]
1019 fn fract_decimal_with_large_negative_exponent() {
1020 let d = Decimal::new(1i64, -39i64);
1023 assert_eq!(d.fract(), Decimal::new(1i64, -39i64));
1024 }
1025}