1use std::{
5 cmp::Ordering,
6 fmt,
7 fmt::{Display, Formatter},
8 hash,
9 str::FromStr,
10};
11
12use arrow_buffer::i256;
13use serde::{
14 Deserialize, Deserializer, Serialize, Serializer,
15 de::{self, Visitor},
16};
17
18use crate::{
19 error::{Error, TypeError},
20 fragment::Fragment,
21 value::value_type::ValueType,
22};
23
24pub mod parse;
25pub mod unscaled;
26
27use unscaled::{MAX_DIGITS, ParseError, Parsed};
28
29#[derive(Clone, Debug, Default)]
30pub struct Decimal {
31 unscaled: i256,
32 scale: u8,
33}
34
35impl Decimal {
36 pub const MAX_SCALE: u8 = MAX_DIGITS;
37
38 pub fn from_parts(unscaled: i256, scale: u8) -> Option<Self> {
39 (scale <= MAX_DIGITS && unscaled::in_range(unscaled)).then_some(Self {
40 unscaled,
41 scale,
42 })
43 }
44
45 pub fn parse(text: &str) -> Result<Self, ParseError> {
46 let (unscaled, scale) = Parsed::parse(text)?.to_decimal()?;
47 Ok(Self {
48 unscaled,
49 scale,
50 })
51 }
52
53 pub fn zero() -> Self {
54 Self::default()
55 }
56
57 pub fn one() -> Self {
58 Self::from_i64(1)
59 }
60
61 pub fn from_i64(value: i64) -> Self {
62 Self::integer(i256::from_i128(value as i128))
63 }
64
65 pub fn from_i128(value: i128) -> Self {
66 Self::integer(i256::from_i128(value))
67 }
68
69 pub fn from_u128(value: u128) -> Self {
70 Self::integer(i256::from_parts(value, 0))
71 }
72
73 pub fn from_f64(value: f64) -> Option<Self> {
74 if !value.is_finite() {
75 return None;
76 }
77 Self::from_float_text(&format!("{value:e}"))
78 }
79
80 pub fn from_f32(value: f32) -> Option<Self> {
81 if !value.is_finite() {
82 return None;
83 }
84 Self::from_float_text(&format!("{value:e}"))
85 }
86
87 pub fn saturated(negative: bool, scale: u8) -> Self {
88 Self {
89 unscaled: if negative {
90 unscaled::MIN
91 } else {
92 unscaled::MAX
93 },
94 scale: scale.min(MAX_DIGITS),
95 }
96 }
97
98 pub fn unscaled(&self) -> i256 {
99 self.unscaled
100 }
101
102 pub fn scale(&self) -> u8 {
103 self.scale
104 }
105
106 pub fn digits(&self) -> u8 {
107 unscaled::digits(self.unscaled)
108 }
109
110 pub fn is_zero(&self) -> bool {
111 self.unscaled == i256::ZERO
112 }
113
114 pub fn is_negative(&self) -> bool {
115 self.unscaled.is_negative()
116 }
117
118 pub fn negate(&self) -> Self {
119 Self {
120 unscaled: self.unscaled.wrapping_neg(),
121 scale: self.scale,
122 }
123 }
124
125 pub fn abs(&self) -> Self {
126 Self {
127 unscaled: self.unscaled.wrapping_abs(),
128 scale: self.scale,
129 }
130 }
131
132 pub fn trunc(&self) -> i256 {
133 match unscaled::pow10(self.scale) {
134 Some(divisor) => self.unscaled.wrapping_div(divisor),
135 None => i256::ZERO,
136 }
137 }
138
139 pub fn ceil(&self) -> i256 {
140 match unscaled::pow10(self.scale) {
141 Some(divisor) if self.unscaled.wrapping_rem(divisor) > i256::ZERO => {
142 self.trunc().wrapping_add(i256::ONE)
143 }
144 _ => self.trunc(),
145 }
146 }
147
148 pub fn floor(&self) -> i256 {
149 match unscaled::pow10(self.scale) {
150 Some(divisor) if self.unscaled.wrapping_rem(divisor) < i256::ZERO => {
151 self.trunc().wrapping_sub(i256::ONE)
152 }
153 _ => self.trunc(),
154 }
155 }
156
157 pub fn to_f64(&self) -> f64 {
158 self.exponent_text().parse().expect("a decimal exponent text always parses as f64")
159 }
160
161 pub fn to_f32(&self) -> f32 {
162 self.exponent_text().parse().expect("a decimal exponent text always parses as f32")
163 }
164
165 pub fn rescale(&self, scale: u8) -> Option<Self> {
166 if scale >= self.scale {
167 let unscaled = unscaled::upscale(self.unscaled, scale - self.scale)?;
168 return Self::from_parts(unscaled, scale);
169 }
170 let divisor = unscaled::pow10(self.scale - scale)?;
171 if self.unscaled.wrapping_rem(divisor) != i256::ZERO {
172 return None;
173 }
174 Self::from_parts(self.unscaled.wrapping_div(divisor), scale)
175 }
176
177 pub fn round_to_scale(&self, scale: u8) -> Option<Self> {
178 if scale >= self.scale {
179 return self.rescale(scale);
180 }
181 Self::from_parts(unscaled::round_half_up(self.unscaled, self.scale - scale), scale)
182 }
183
184 pub fn fits(&self, precision: u8, scale: u8) -> Option<Self> {
185 let rescaled = self.rescale(scale)?;
186 (rescaled.digits() <= precision).then_some(rescaled)
187 }
188
189 pub fn checked_add(&self, other: &Self) -> Option<Self> {
190 let scale = self.scale.max(other.scale);
191 let left = unscaled::upscale(self.unscaled, scale - self.scale)?;
192 let right = unscaled::upscale(other.unscaled, scale - other.scale)?;
193 Self::from_parts(left.checked_add(right)?, scale)
194 }
195
196 pub fn checked_sub(&self, other: &Self) -> Option<Self> {
197 self.checked_add(&other.negate())
198 }
199
200 pub fn checked_mul(&self, other: &Self) -> Option<Self> {
201 let scale = self.scale.checked_add(other.scale)?;
202 if let Some(product) =
203 self.unscaled.checked_mul(other.unscaled).and_then(|unscaled| Self::from_parts(unscaled, scale))
204 {
205 return Some(product);
206 }
207 let ((left, left_scale), (right, right_scale)) = (self.normalized(), other.normalized());
208 Self::from_parts(left.checked_mul(right)?, left_scale + right_scale)
209 }
210
211 pub fn checked_pow(&self, exp: u32) -> Option<Self> {
212 let (unscaled, scale) = self.normalized();
213 let scale = u8::try_from(u32::from(scale).checked_mul(exp)?).ok()?;
214 Self::from_parts(unscaled.checked_pow(exp)?, scale)
215 }
216
217 pub fn checked_div(&self, other: &Self) -> Option<Self> {
218 if other.is_zero() {
219 return None;
220 }
221 let scale = self.scale.max(other.scale).max(6);
222 let extra = u32::from(scale - self.scale) + u32::from(other.scale);
223 let magnitude =
224 unscaled::divide_half_up(self.unscaled.wrapping_abs(), other.unscaled.wrapping_abs(), extra)?;
225 let unscaled = if self.is_negative() != other.is_negative() {
226 magnitude.wrapping_neg()
227 } else {
228 magnitude
229 };
230 Self::from_parts(unscaled, scale)
231 }
232
233 pub fn checked_rem(&self, other: &Self) -> Option<Self> {
234 if other.is_zero() {
235 return None;
236 }
237 let scale = self.scale.max(other.scale);
238 let dividend = self.unscaled.wrapping_abs();
239 let magnitude = match unscaled::upscale(other.unscaled.wrapping_abs(), scale - other.scale) {
240 Some(divisor) => {
241 unscaled::shifted_remainder(dividend.wrapping_rem(divisor), divisor, scale - self.scale)
242 }
243 None => unscaled::upscale(dividend, scale - self.scale)?,
244 };
245 let unscaled = if self.is_negative() {
246 magnitude.wrapping_neg()
247 } else {
248 magnitude
249 };
250 Self::from_parts(unscaled, scale)
251 }
252
253 pub fn saturating_add(&self, other: &Self) -> Self {
254 self.checked_add(other)
255 .unwrap_or_else(|| Self::saturated(*self < other.negate(), self.scale.max(other.scale)))
256 }
257
258 pub fn saturating_sub(&self, other: &Self) -> Self {
259 self.checked_sub(other).unwrap_or_else(|| Self::saturated(*self < *other, self.scale.max(other.scale)))
260 }
261
262 pub fn saturating_mul(&self, other: &Self) -> Self {
263 if let Some(product) = self.checked_mul(other) {
264 return product;
265 }
266 let scale = u16::from(self.scale) + u16::from(other.scale);
267 if scale > u16::from(MAX_DIGITS)
268 && let Some(product) = self.unscaled.checked_mul(other.unscaled)
269 {
270 let drop = u8::try_from(scale - u16::from(MAX_DIGITS)).unwrap_or(u8::MAX);
271 return Self {
272 unscaled: unscaled::round_half_up(product, drop),
273 scale: MAX_DIGITS,
274 };
275 }
276 Self::saturated(self.is_negative() != other.is_negative(), scale.min(u16::from(MAX_DIGITS)) as u8)
277 }
278
279 pub fn saturating_div(&self, other: &Self) -> Self {
280 let scale = self.scale.max(other.scale).max(6);
281 if other.is_zero() {
282 return Self {
283 unscaled: i256::ZERO,
284 scale,
285 };
286 }
287 self.checked_div(other)
288 .unwrap_or_else(|| Self::saturated(self.is_negative() != other.is_negative(), scale))
289 }
290
291 pub fn saturating_rem(&self, other: &Self) -> Self {
292 if other.is_zero() {
293 return Self::zero();
294 }
295 self.checked_rem(other)
296 .unwrap_or_else(|| Self::saturated(self.is_negative(), self.scale.max(other.scale)))
297 }
298
299 fn integer(unscaled: i256) -> Self {
300 Self {
301 unscaled,
302 scale: 0,
303 }
304 }
305
306 fn from_float_text(text: &str) -> Option<Self> {
307 let (unscaled, scale) = Parsed::parse(text).ok()?.to_rounded_decimal().ok()?;
308 Self::from_parts(unscaled, scale)
309 }
310
311 fn exponent_text(&self) -> String {
312 format!("{}e-{}", self.unscaled, self.scale)
313 }
314
315 fn normalized(&self) -> (i256, u8) {
316 let ten = i256::from_i128(10);
317 let mut unscaled = self.unscaled;
318 let mut scale = self.scale;
319 while scale > 0 && unscaled.wrapping_rem(ten) == i256::ZERO {
320 unscaled = unscaled.wrapping_div(ten);
321 scale -= 1;
322 }
323 (unscaled, scale)
324 }
325}
326
327impl PartialEq for Decimal {
328 fn eq(&self, other: &Self) -> bool {
329 self.normalized() == other.normalized()
330 }
331}
332
333impl Eq for Decimal {}
334
335impl PartialOrd for Decimal {
336 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
337 Some(self.cmp(other))
338 }
339}
340
341impl Ord for Decimal {
342 fn cmp(&self, other: &Self) -> Ordering {
343 let scale = self.scale.max(other.scale);
344 let left = unscaled::upscale(self.unscaled, scale - self.scale);
345 let right = unscaled::upscale(other.unscaled, scale - other.scale);
346 match (left, right) {
347 (Some(left), Some(right)) => left.cmp(&right),
348 (None, _) if self.is_negative() => Ordering::Less,
349 (None, _) => Ordering::Greater,
350 (_, None) if other.is_negative() => Ordering::Greater,
351 (_, None) => Ordering::Less,
352 }
353 }
354}
355
356impl hash::Hash for Decimal {
357 fn hash<H: hash::Hasher>(&self, state: &mut H) {
358 self.normalized().hash(state);
359 }
360}
361
362impl Display for Decimal {
363 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
364 let magnitude = self.unscaled.wrapping_abs().to_string();
365 let sign = if self.is_negative() {
366 "-"
367 } else {
368 ""
369 };
370 let scale = self.scale as usize;
371 if scale == 0 {
372 return write!(f, "{sign}{magnitude}");
373 }
374 let padded = if magnitude.len() <= scale {
375 format!("{}{}", "0".repeat(scale + 1 - magnitude.len()), magnitude)
376 } else {
377 magnitude
378 };
379 let (integer, fraction) = padded.split_at(padded.len() - scale);
380 write!(f, "{sign}{integer}.{fraction}")
381 }
382}
383
384impl FromStr for Decimal {
385 type Err = Error;
386
387 fn from_str(s: &str) -> Result<Self, Self::Err> {
388 Self::parse(s).map_err(|error| parse_error(error, Fragment::None))
389 }
390}
391
392pub(crate) fn parse_error(error: ParseError, fragment: Fragment) -> Error {
393 match error {
394 ParseError::Invalid => TypeError::InvalidNumberFormat {
395 target: ValueType::DECIMAL,
396 fragment,
397 }
398 .into(),
399 ParseError::OutOfRange => TypeError::NumberOutOfRange {
400 target: ValueType::DECIMAL,
401 fragment,
402 descriptor: None,
403 }
404 .into(),
405 }
406}
407
408impl From<i8> for Decimal {
409 fn from(value: i8) -> Self {
410 Self::from_i128(value as i128)
411 }
412}
413
414impl From<i16> for Decimal {
415 fn from(value: i16) -> Self {
416 Self::from_i128(value as i128)
417 }
418}
419
420impl From<i32> for Decimal {
421 fn from(value: i32) -> Self {
422 Self::from_i128(value as i128)
423 }
424}
425
426impl From<i64> for Decimal {
427 fn from(value: i64) -> Self {
428 Self::from_i128(value as i128)
429 }
430}
431
432impl From<i128> for Decimal {
433 fn from(value: i128) -> Self {
434 Self::from_i128(value)
435 }
436}
437
438impl From<u8> for Decimal {
439 fn from(value: u8) -> Self {
440 Self::from_i128(value as i128)
441 }
442}
443
444impl From<u16> for Decimal {
445 fn from(value: u16) -> Self {
446 Self::from_i128(value as i128)
447 }
448}
449
450impl From<u32> for Decimal {
451 fn from(value: u32) -> Self {
452 Self::from_i128(value as i128)
453 }
454}
455
456impl From<u64> for Decimal {
457 fn from(value: u64) -> Self {
458 Self::from_i128(value as i128)
459 }
460}
461
462impl From<u128> for Decimal {
463 fn from(value: u128) -> Self {
464 Self::from_u128(value)
465 }
466}
467
468impl From<f32> for Decimal {
469 fn from(value: f32) -> Self {
470 if !value.is_finite() {
471 return Self::zero();
472 }
473 Self::from_f32(value).unwrap_or_else(|| Self::saturated(value < 0.0, 0))
474 }
475}
476
477impl From<f64> for Decimal {
478 fn from(value: f64) -> Self {
479 if !value.is_finite() {
480 return Self::zero();
481 }
482 Self::from_f64(value).unwrap_or_else(|| Self::saturated(value < 0.0, 0))
483 }
484}
485
486impl Serialize for Decimal {
487 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
488 where
489 S: Serializer,
490 {
491 serializer.collect_str(self)
492 }
493}
494
495struct DecimalVisitor;
496
497impl<'de> Visitor<'de> for DecimalVisitor {
498 type Value = Decimal;
499
500 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
501 formatter.write_str("a decimal number as a string")
502 }
503
504 fn visit_str<E>(self, value: &str) -> Result<Decimal, E>
505 where
506 E: de::Error,
507 {
508 Decimal::parse(value).map_err(|error| E::custom(format!("invalid decimal {value:?}: {error:?}")))
509 }
510}
511
512impl<'de> Deserialize<'de> for Decimal {
513 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
514 where
515 D: Deserializer<'de>,
516 {
517 deserializer.deserialize_str(DecimalVisitor)
518 }
519}
520
521#[cfg(test)]
522pub mod tests {
523 use std::{
524 collections::HashSet,
525 hash::{DefaultHasher, Hash, Hasher},
526 };
527
528 use postcard::{from_bytes, to_stdvec};
529 use serde_json::{from_str, to_string};
530
531 use super::*;
532
533 fn dec(text: &str) -> Decimal {
534 Decimal::from_str(text).unwrap()
535 }
536
537 fn hash_of(decimal: &Decimal) -> u64 {
538 let mut hasher = DefaultHasher::new();
539 decimal.hash(&mut hasher);
540 hasher.finish()
541 }
542
543 #[test]
544 fn test_new_decimal_valid() {
545 let decimal = Decimal::from_parts(i256::from_i128(12345), 2).unwrap();
546 assert_eq!(decimal.to_string(), "123.45");
547 }
548
549 #[test]
550 fn test_from_str() {
551 let decimal = Decimal::from_str("123.45").unwrap();
552 assert_eq!(decimal.to_string(), "123.45");
553 }
554
555 #[test]
556 fn test_comparison() {
557 let d1 = Decimal::from_str("123.45").unwrap();
558 let d2 = Decimal::from_str("123.46").unwrap();
559 let d3 = Decimal::from_str("123.45").unwrap();
560
561 assert!(d1 < d2);
562 assert_eq!(d1, d3);
563 }
564
565 #[test]
566 fn test_display() {
567 let decimal = Decimal::from_str("123.45").unwrap();
568 assert_eq!(format!("{}", decimal), "123.45");
569 }
570
571 #[test]
572 fn test_serde_json() {
573 let decimal = Decimal::from_str("123.456789").unwrap();
574 let json = to_string(&decimal).unwrap();
575 assert_eq!(json, "\"123.456789\"");
576
577 let deserialized: Decimal = from_str(&json).unwrap();
578 assert_eq!(deserialized, decimal);
579 }
580
581 #[test]
582 fn test_serde_json_negative() {
583 let decimal = Decimal::from_str("-987.654321").unwrap();
584 let json = to_string(&decimal).unwrap();
585 assert_eq!(json, "\"-987.654321\"");
586
587 let deserialized: Decimal = from_str(&json).unwrap();
588 assert_eq!(deserialized, decimal);
589 }
590
591 #[test]
592 fn test_serde_json_zero() {
593 let decimal = Decimal::zero();
594 let json = to_string(&decimal).unwrap();
595 assert_eq!(json, "\"0\"");
596
597 let deserialized: Decimal = from_str(&json).unwrap();
598 assert_eq!(deserialized, decimal);
599 }
600
601 #[test]
602 fn test_serde_json_zero_with_scale() {
603 let decimal = Decimal::from_str("0.00").unwrap();
605 let json = to_string(&decimal).unwrap();
606 assert_eq!(json, "\"0.00\"");
607
608 let deserialized: Decimal = from_str(&json).unwrap();
609 assert_eq!(deserialized, decimal);
610 assert_eq!(deserialized.to_string(), "0.00");
611 }
612
613 #[test]
614 fn test_serde_json_high_precision() {
615 let decimal = Decimal::from_str("123456789.123456789123456789").unwrap();
616 let json = to_string(&decimal).unwrap();
617
618 let deserialized: Decimal = from_str(&json).unwrap();
619 assert_eq!(deserialized, decimal);
620 }
621
622 #[test]
623 fn test_serde_postcard() {
624 let decimal = Decimal::from_str("123.456789").unwrap();
625 let encoded = to_stdvec(&decimal).unwrap();
626
627 let decoded: Decimal = from_bytes(&encoded).unwrap();
628 assert_eq!(decoded, decimal);
629 }
630
631 #[test]
632 fn test_serde_postcard_negative() {
633 let decimal = Decimal::from_str("-987.654321").unwrap();
634 let encoded = to_stdvec(&decimal).unwrap();
635
636 let decoded: Decimal = from_bytes(&encoded).unwrap();
637 assert_eq!(decoded, decimal);
638 }
639
640 #[test]
641 fn test_serde_postcard_zero() {
642 let decimal = Decimal::zero();
643 let encoded = to_stdvec(&decimal).unwrap();
644
645 let decoded: Decimal = from_bytes(&encoded).unwrap();
646 assert_eq!(decoded, decimal);
647 }
648
649 #[test]
650 fn test_serde_postcard_high_precision() {
651 let decimal = Decimal::from_str("123456789.123456789123456789").unwrap();
652 let encoded = to_stdvec(&decimal).unwrap();
653
654 let decoded: Decimal = from_bytes(&encoded).unwrap();
655 assert_eq!(decoded, decimal);
656 }
657
658 #[test]
659 fn test_serde_postcard_large_number() {
660 let decimal = Decimal::from_str("999999999999999999999999999999.999999999999999999999999").unwrap();
661 let encoded = to_stdvec(&decimal).unwrap();
662
663 let decoded: Decimal = from_bytes(&encoded).unwrap();
664 assert_eq!(decoded, decimal);
665 }
666
667 #[test]
668 fn serde_keeps_the_scale_not_only_the_value() {
669 for text in ["1.50", "-0.120", "0.00", "7"] {
671 let decimal = dec(text);
672 let back: Decimal = from_bytes(&to_stdvec(&decimal).unwrap()).unwrap();
673 assert_eq!((back.unscaled(), back.scale()), (decimal.unscaled(), decimal.scale()), "{text}");
674 let back: Decimal = from_str(&to_string(&decimal).unwrap()).unwrap();
675 assert_eq!(back.to_string(), text);
676 }
677 }
678
679 #[test]
680 fn display_prints_exactly_scale_fraction_digits() {
681 assert_eq!(Decimal::from_parts(i256::ZERO, 2).unwrap().to_string(), "0.00");
683 assert_eq!(Decimal::from_parts(i256::from_i128(5), 3).unwrap().to_string(), "0.005");
684 assert_eq!(Decimal::from_parts(i256::from_i128(-5), 3).unwrap().to_string(), "-0.005");
685 assert_eq!(Decimal::from_parts(i256::from_i128(-12345), 3).unwrap().to_string(), "-12.345");
686 assert_eq!(Decimal::from_parts(i256::from_i128(1000), 0).unwrap().to_string(), "1000");
687 assert_eq!(
688 Decimal::from_parts(unscaled::MIN, 76).unwrap().to_string(),
689 format!("-0.{}", "9".repeat(76))
690 );
691 }
692
693 #[test]
694 fn from_parts_rejects_seventy_seven_digits_and_scales_past_seventy_six() {
695 assert!(Decimal::from_parts(unscaled::MAX, 76).is_some());
697 assert_eq!(Decimal::from_parts(unscaled::MAX.wrapping_add(i256::ONE), 0), None);
698 assert_eq!(Decimal::from_parts(unscaled::MIN.wrapping_sub(i256::ONE), 0), None);
699 assert_eq!(Decimal::from_parts(i256::ONE, 77), None);
700 }
701
702 #[test]
703 fn equality_order_and_hash_ignore_the_scale() {
704 assert_eq!(dec("1.5"), dec("1.50"));
706 assert_eq!(hash_of(&dec("1.5")), hash_of(&dec("1.500")));
707 assert_eq!(dec("0"), dec("0.000"));
708 assert_eq!(hash_of(&dec("0")), hash_of(&dec("0.000")));
709 assert_eq!(dec("1.5").cmp(&dec("1.50")), Ordering::Equal);
710 assert!(dec("-1.51") < dec("-1.5"));
711 assert!(dec("0.1") < dec("0.10000000001"));
712 assert_ne!(dec("1.5"), dec("15"));
713 let set: HashSet<Decimal> = ["1.5", "1.50", "1.500"].iter().map(|s| dec(s)).collect();
714 assert_eq!(set.len(), 1);
715 }
716
717 #[test]
718 fn order_holds_when_rescaling_would_overflow() {
719 let huge = Decimal::from_parts(unscaled::MAX, 0).unwrap();
721 let tiny = Decimal::from_parts(i256::ONE, 76).unwrap();
722 assert!(huge > tiny);
723 assert!(huge.negate() < tiny);
724 assert!(tiny < huge);
725 assert!(tiny > huge.negate());
726 assert_ne!(huge, tiny);
727 }
728
729 #[test]
730 fn from_str_rejects_past_seventy_six_digits_as_out_of_range() {
731 assert_eq!(Decimal::from_str(&"1".repeat(77)).unwrap_err().code, "NUMBER_002");
733 assert_eq!(Decimal::from_str("1.2.3").unwrap_err().code, "NUMBER_001");
734 assert!(Decimal::from_str(&"9".repeat(76)).is_ok());
735 }
736
737 #[test]
738 fn checked_add_and_sub_use_the_larger_scale() {
739 let sum = dec("1.5").checked_add(&dec("2.25")).unwrap();
741 assert_eq!((sum.to_string(), sum.scale()), ("3.75".to_string(), 2));
742 assert_eq!(dec("1").checked_sub(&dec("0.001")).unwrap().to_string(), "0.999");
743 let max = Decimal::from_parts(unscaled::MAX, 0).unwrap();
744 assert_eq!(max.checked_add(&Decimal::one()), None);
745 assert_eq!(max.negate().checked_sub(&Decimal::one()), None);
746 assert_eq!(max.checked_sub(&Decimal::one()).unwrap().digits(), 76);
747 }
748
749 #[test]
750 fn checked_mul_adds_the_scales() {
751 assert_eq!(dec("1.50").checked_mul(&dec("2.0")).unwrap().to_string(), "3.000");
753 assert_eq!(dec("-1.5").checked_mul(&dec("1.5")).unwrap().to_string(), "-2.25");
754 let wide = Decimal::from_parts(unscaled::pow10(40).unwrap(), 0).unwrap();
755 assert_eq!(wide.checked_mul(&wide), None);
756 let deep = Decimal::from_parts(i256::ONE, 40).unwrap();
757 assert_eq!(deep.checked_mul(&deep), None);
758 }
759
760 #[test]
761 fn checked_div_uses_at_least_six_places_and_rounds_half_up() {
762 assert_eq!(dec("2").checked_div(&dec("3")).unwrap().to_string(), "0.666667");
764 assert_eq!(dec("-2").checked_div(&dec("3")).unwrap().to_string(), "-0.666667");
765 assert_eq!(dec("3").checked_div(&dec("2")).unwrap().to_string(), "1.500000");
766 assert_eq!(dec("1.00000000").checked_div(&dec("4")).unwrap().to_string(), "0.25000000");
767 assert_eq!(dec("10").checked_div(&dec("0.001")).unwrap().to_string(), "10000.000000");
768 assert_eq!(dec("1").checked_div(&dec("0")), None);
769 let huge = Decimal::from_parts(unscaled::pow10(70).unwrap(), 0).unwrap();
770 assert_eq!(huge.checked_div(&dec("3")).unwrap().digits(), 76);
771 assert_eq!(huge.checked_div(&dec("0.1")), None);
772 }
773
774 #[test]
775 fn checked_rem_keeps_the_dividend_sign_and_the_larger_scale() {
776 assert_eq!(dec("7.5").checked_rem(&dec("2")).unwrap().to_string(), "1.5");
778 assert_eq!(dec("-7.5").checked_rem(&dec("2")).unwrap().to_string(), "-1.5");
779 assert_eq!(dec("7").checked_rem(&dec("0.3")).unwrap().to_string(), "0.1");
780 assert_eq!(dec("7").checked_rem(&dec("0")), None);
781 let huge = Decimal::from_parts(unscaled::MAX, 0).unwrap();
782 let tenths = Decimal::from_parts(i256::from_i128(3), 1).unwrap();
783 assert_eq!(huge.checked_rem(&tenths).unwrap().to_string(), "0.0");
784 let small = dec("0.5");
785 assert_eq!(small.checked_rem(&huge).unwrap().to_string(), "0.5");
786 assert_eq!(dec("-0.5").checked_rem(&huge).unwrap().to_string(), "-0.5");
787 }
788
789 #[test]
790 fn round_to_scale_rounds_half_away_from_zero() {
791 assert_eq!(dec("1.235").round_to_scale(2).unwrap().to_string(), "1.24");
793 assert_eq!(dec("-1.235").round_to_scale(2).unwrap().to_string(), "-1.24");
794 assert_eq!(dec("1.234").round_to_scale(2).unwrap().to_string(), "1.23");
795 assert_eq!(dec("0.125").round_to_scale(2).unwrap().to_string(), "0.13");
796 assert_eq!(dec("0.0000001").round_to_scale(2).unwrap().to_string(), "0.00");
797 assert_eq!(dec("1.5").round_to_scale(3).unwrap().to_string(), "1.500");
798 let max = Decimal::from_parts(unscaled::MAX, 1).unwrap();
799 assert_eq!(max.round_to_scale(0), Decimal::from_parts(unscaled::pow10(75).unwrap(), 0), "a carry fits");
800 let widest = Decimal::from_parts(unscaled::MAX, 0).unwrap();
801 assert_eq!(widest.round_to_scale(1), None, "padding 76 nines with a zero needs a 77th digit");
802 }
803
804 #[test]
805 fn rescale_refuses_to_drop_fraction_digits() {
806 assert_eq!(dec("1.235").rescale(2), None);
808 assert_eq!(dec("1.230").rescale(2).unwrap().to_string(), "1.23");
809 assert_eq!(dec("1.2").rescale(2).unwrap().to_string(), "1.20");
810 assert_eq!(Decimal::from_parts(unscaled::MAX, 0).unwrap().rescale(1), None);
811 assert_eq!(dec("1.2").fits(10, 2).unwrap().to_string(), "1.20");
812 assert_eq!(dec("123456789.99").fits(10, 2), None);
813 assert_eq!(dec("12345678.99").fits(10, 2).unwrap().to_string(), "12345678.99");
814 assert_eq!(dec("1.235").fits(10, 2), None);
815 }
816
817 #[test]
818 fn float_conversions_go_through_the_shortest_text() {
819 assert_eq!(Decimal::from_f64(0.1).unwrap().to_string(), "0.1");
821 assert_eq!(Decimal::from_f64(-2.5e-3).unwrap().to_string(), "-0.0025");
822 assert_eq!(Decimal::from_f64(1e20).unwrap().to_string(), "100000000000000000000");
823 assert_eq!(Decimal::from_f64(1e-300).unwrap(), Decimal::zero());
824 assert_eq!(Decimal::from_f64(1e80), None);
825 assert_eq!(Decimal::from_f64(f64::NAN), None);
826 assert_eq!(Decimal::from_f32(1.5f32).unwrap().to_string(), "1.5");
827 assert_eq!(Decimal::from(f64::INFINITY), Decimal::zero());
828 assert_eq!(Decimal::from(-1e80f64), Decimal::saturated(true, 0));
829 assert_eq!(dec("0.1").to_f64(), 0.1);
830 assert_eq!(dec("-12.5").to_f32(), -12.5f32);
831 assert_eq!(Decimal::from_parts(unscaled::MAX, 0).unwrap().to_f64(), 1e76);
832 }
833
834 #[test]
835 fn trunc_drops_the_fraction_toward_zero() {
836 assert_eq!(dec("3.7").trunc(), i256::from_i128(3));
838 assert_eq!(dec("-3.7").trunc(), i256::from_i128(-3));
839 assert_eq!(dec("0.999").trunc(), i256::ZERO);
840 }
841
842 #[test]
843 fn ceil_rounds_toward_positive_infinity_exactly() {
844 assert_eq!(dec("3.2").ceil(), i256::from_i128(4));
846 assert_eq!(dec("-3.7").ceil(), i256::from_i128(-3));
847 assert_eq!(dec("5.000").ceil(), i256::from_i128(5));
848 assert_eq!(dec("0.001").ceil(), i256::ONE);
849 assert_eq!(dec("-0.001").ceil(), i256::ZERO);
850 assert_eq!(
851 dec("12345678901234567890123456789.5").ceil(),
852 dec("12345678901234567890123456790").unscaled()
853 );
854 }
855
856 #[test]
857 fn floor_rounds_toward_negative_infinity_exactly() {
858 assert_eq!(dec("3.7").floor(), i256::from_i128(3));
860 assert_eq!(dec("-3.2").floor(), i256::from_i128(-4));
861 assert_eq!(dec("-5.00").floor(), i256::from_i128(-5));
862 assert_eq!(dec("-0.001").floor(), i256::MINUS_ONE);
863 assert_eq!(dec("0.001").floor(), i256::ZERO);
864 assert_eq!(
865 dec("-12345678901234567890123456789.5").floor(),
866 dec("-12345678901234567890123456790").unscaled()
867 );
868 }
869
870 #[test]
871 fn checked_pow_is_exact_and_refuses_what_does_not_fit() {
872 assert_eq!(
874 dec("1234567890123456789.25").checked_pow(2),
875 Some(dec("1524157875323883675636335943811918915.5625"))
876 );
877 assert_eq!(dec("2.50").checked_pow(3), Some(dec("15.625")));
878 assert_eq!(dec("-1.5").checked_pow(3), Some(dec("-3.375")));
879 assert_eq!(dec("7.25").checked_pow(0), Some(Decimal::one()));
880 assert_eq!(dec("10").checked_pow(80), None);
881 assert_eq!(dec("0.5").checked_pow(200), None);
882 }
883
884 #[test]
885 fn saturating_ops_clamp_to_seventy_six_nines_with_the_exact_sign() {
886 let max = Decimal::from_parts(unscaled::MAX, 0).unwrap();
888 let min = max.negate();
889 assert_eq!(max.saturating_add(&Decimal::one()), max);
890 assert_eq!(min.saturating_add(&Decimal::one().negate()), min);
891 assert_eq!(min.saturating_sub(&Decimal::one()), min);
892 assert_eq!(max.saturating_sub(&Decimal::one().negate()), max);
893 assert_eq!(max.saturating_add(&dec("0.5")), Decimal::saturated(false, 1));
894 assert_eq!(max.saturating_mul(&dec("-2")), min);
895 assert_eq!(min.saturating_mul(&dec("-2")), max);
896 assert_eq!(dec("1").saturating_div(&dec("0")).to_string(), "0.000000");
897 assert_eq!(max.saturating_div(&dec("0.5")), Decimal::saturated(false, 6));
898 assert_eq!(dec("7").saturating_rem(&dec("0")), Decimal::zero());
899 assert_eq!(dec("1.5").saturating_add(&dec("1")).to_string(), "2.5");
900 }
901
902 #[test]
903 fn saturating_mul_rounds_a_scale_past_seventy_six_instead_of_clamping() {
904 let deep = Decimal::from_parts(i256::from_i128(15), 40).unwrap();
906 let product = deep.saturating_mul(&deep);
907 assert_eq!(product.scale(), 76);
908 assert_eq!(product, Decimal::zero());
909 let half = Decimal::from_parts(i256::from_i128(5), 40).unwrap();
910 let unit = Decimal::from_parts(i256::ONE, 37).unwrap();
911 assert_eq!(half.saturating_mul(&unit), Decimal::from_parts(i256::ONE, 76).unwrap());
912 assert_eq!(half.negate().saturating_mul(&unit), Decimal::from_parts(i256::MINUS_ONE, 76).unwrap());
913 }
914}