oxsdatatypes/
decimal.rs

1use crate::{Boolean, Double, Float, Integer, TooLargeForIntegerError};
2use std::fmt;
3use std::fmt::Write;
4use std::str::FromStr;
5
6const DECIMAL_PART_DIGITS: u32 = 18;
7const DECIMAL_PART_POW: i128 = 1_000_000_000_000_000_000;
8const DECIMAL_PART_POW_MINUS_ONE: i128 = 100_000_000_000_000_000;
9
10/// [XML Schema `decimal` datatype](https://www.w3.org/TR/xmlschema11-2/#decimal)
11///
12/// It stores the decimal in a fix point encoding allowing nearly 18 digits before and 18 digits after ".".
13///
14/// It stores the value in a [`i128`] integer after multiplying it by 10¹⁸.
15#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash, Default)]
16#[repr(Rust, packed(8))]
17pub struct Decimal {
18    value: i128, // value * 10^18
19}
20
21impl Decimal {
22    pub const MAX: Self = Self { value: i128::MAX };
23    pub const MIN: Self = Self { value: i128::MIN };
24    #[cfg(test)]
25    pub const STEP: Self = Self { value: 1 };
26
27    /// Constructs the decimal i / 10^n
28    #[inline]
29    pub const fn new(i: i128, n: u32) -> Result<Self, TooLargeForDecimalError> {
30        let Some(shift) = DECIMAL_PART_DIGITS.checked_sub(n) else {
31            return Err(TooLargeForDecimalError);
32        };
33        let Some(value) = i.checked_mul(10_i128.pow(shift)) else {
34            return Err(TooLargeForDecimalError);
35        };
36        Ok(Self { value })
37    }
38
39    pub(crate) const fn new_from_i128_unchecked(value: i128) -> Self {
40        Self {
41            value: value * DECIMAL_PART_POW,
42        }
43    }
44
45    #[inline]
46    #[must_use]
47    pub fn from_be_bytes(bytes: [u8; 16]) -> Self {
48        Self {
49            value: i128::from_be_bytes(bytes),
50        }
51    }
52
53    #[inline]
54    #[must_use]
55    pub fn to_be_bytes(self) -> [u8; 16] {
56        self.value.to_be_bytes()
57    }
58
59    /// [op:numeric-add](https://www.w3.org/TR/xpath-functions-31/#func-numeric-add)
60    ///
61    /// Returns `None` in case of overflow ([FOAR0002](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0002)).
62    #[inline]
63    #[must_use]
64    pub fn checked_add(self, rhs: impl Into<Self>) -> Option<Self> {
65        Some(Self {
66            value: self.value.checked_add(rhs.into().value)?,
67        })
68    }
69
70    /// [op:numeric-subtract](https://www.w3.org/TR/xpath-functions-31/#func-numeric-subtract)
71    ///
72    /// Returns `None` in case of overflow ([FOAR0002](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0002)).
73    #[inline]
74    #[must_use]
75    pub fn checked_sub(self, rhs: impl Into<Self>) -> Option<Self> {
76        Some(Self {
77            value: self.value.checked_sub(rhs.into().value)?,
78        })
79    }
80
81    /// [op:numeric-multiply](https://www.w3.org/TR/xpath-functions-31/#func-numeric-multiply)
82    ///
83    /// Returns `None` in case of overflow ([FOAR0002](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0002)).
84    #[inline]
85    #[must_use]
86    pub fn checked_mul(self, rhs: impl Into<Self>) -> Option<Self> {
87        // Idea: we shift right as much as possible to keep as much precision as possible
88        // Do the multiplication and do the required left shift
89        let mut left = self.value;
90        let mut shift_left = 0_u32;
91        if left != 0 {
92            while left % 10 == 0 {
93                left /= 10;
94                shift_left += 1;
95            }
96        }
97
98        let mut right = rhs.into().value;
99        let mut shift_right = 0_u32;
100        if right != 0 {
101            while right % 10 == 0 {
102                right /= 10;
103                shift_right += 1;
104            }
105        }
106
107        // We do multiplication + shift
108        let shift = (shift_left + shift_right).checked_sub(DECIMAL_PART_DIGITS)?;
109        Some(Self {
110            value: left
111                .checked_mul(right)?
112                .checked_mul(10_i128.checked_pow(shift)?)?,
113        })
114    }
115
116    /// [op:numeric-divide](https://www.w3.org/TR/xpath-functions-31/#func-numeric-divide)
117    ///
118    /// Returns `None` in case of division by 0 ([FOAR0001](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0001)) or overflow ([FOAR0002](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0002)).
119    #[inline]
120    #[must_use]
121    pub fn checked_div(self, rhs: impl Into<Self>) -> Option<Self> {
122        // Idea: we shift the dividend left as much as possible to keep as much precision as possible
123        // And we shift right the divisor as much as possible
124        // Do the multiplication and do the required shift
125        let mut left = self.value;
126        let mut shift_left = 0_u32;
127        if left != 0 {
128            while let Some(r) = left.checked_mul(10) {
129                left = r;
130                shift_left += 1;
131            }
132        }
133        let mut right = rhs.into().value;
134        let mut shift_right = 0_u32;
135        if right != 0 {
136            while right % 10 == 0 {
137                right /= 10;
138                shift_right += 1;
139            }
140        }
141
142        // We do division + shift
143        let shift = (shift_left + shift_right).checked_sub(DECIMAL_PART_DIGITS)?;
144        Some(Self {
145            value: left
146                .checked_div(right)?
147                .checked_div(10_i128.checked_pow(shift)?)?,
148        })
149    }
150
151    /// [op:numeric-mod](https://www.w3.org/TR/xpath-functions-31/#func-numeric-mod)
152    ///
153    /// Returns `None` in case of division by 0 ([FOAR0001](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0001)) or overflow ([FOAR0002](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0002)).
154    #[inline]
155    #[must_use]
156    pub fn checked_rem(self, rhs: impl Into<Self>) -> Option<Self> {
157        Some(Self {
158            value: self.value.checked_rem(rhs.into().value)?,
159        })
160    }
161
162    /// Euclidean remainder
163    ///
164    /// Returns `None` in case of division by 0 ([FOAR0001](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0001)) or overflow ([FOAR0002](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0002)).
165    #[inline]
166    #[must_use]
167    pub fn checked_rem_euclid(self, rhs: impl Into<Self>) -> Option<Self> {
168        Some(Self {
169            value: self.value.checked_rem_euclid(rhs.into().value)?,
170        })
171    }
172
173    /// [op:numeric-unary-minus](https://www.w3.org/TR/xpath-functions-31/#func-numeric-unary-minus)
174    ///
175    /// Returns `None` in case of overflow ([FOAR0002](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0002)).
176    #[inline]
177    #[must_use]
178    pub fn checked_neg(self) -> Option<Self> {
179        Some(Self {
180            value: self.value.checked_neg()?,
181        })
182    }
183
184    /// [fn:abs](https://www.w3.org/TR/xpath-functions-31/#func-abs)
185    ///
186    /// Returns `None` in case of overflow ([FOAR0002](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0002)).
187    #[inline]
188    #[must_use]
189    pub fn checked_abs(self) -> Option<Self> {
190        Some(Self {
191            value: self.value.checked_abs()?,
192        })
193    }
194
195    /// [fn:round](https://www.w3.org/TR/xpath-functions-31/#func-round)
196    ///
197    /// Returns `None` in case of overflow ([FOAR0002](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0002)).
198    #[inline]
199    #[must_use]
200    pub fn checked_round(self) -> Option<Self> {
201        let value = self.value / DECIMAL_PART_POW_MINUS_ONE;
202        Some(Self {
203            value: if value >= 0 {
204                value / 10 + i128::from(value % 10 >= 5)
205            } else {
206                value / 10 - i128::from(-value % 10 > 5)
207            }
208            .checked_mul(DECIMAL_PART_POW)?,
209        })
210    }
211
212    /// [fn:ceiling](https://www.w3.org/TR/xpath-functions-31/#func-ceiling)
213    ///
214    /// Returns `None` in case of overflow ([FOAR0002](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0002)).
215    #[inline]
216    #[must_use]
217    pub fn checked_ceil(self) -> Option<Self> {
218        Some(Self {
219            value: if self.value > 0 && self.value % DECIMAL_PART_POW != 0 {
220                self.value / DECIMAL_PART_POW + 1
221            } else {
222                self.value / DECIMAL_PART_POW
223            }
224            .checked_mul(DECIMAL_PART_POW)?,
225        })
226    }
227
228    /// [fn:floor](https://www.w3.org/TR/xpath-functions-31/#func-floor)
229    ///
230    /// Returns `None` in case of overflow ([FOAR0002](https://www.w3.org/TR/xpath-functions-31/#ERRFOAR0002)).
231    #[inline]
232    #[must_use]
233    pub fn checked_floor(self) -> Option<Self> {
234        Some(Self {
235            value: if self.value >= 0 || self.value % DECIMAL_PART_POW == 0 {
236                self.value / DECIMAL_PART_POW
237            } else {
238                self.value / DECIMAL_PART_POW - 1
239            }
240            .checked_mul(DECIMAL_PART_POW)?,
241        })
242    }
243
244    #[inline]
245    #[must_use]
246    pub const fn is_negative(self) -> bool {
247        self.value < 0
248    }
249
250    #[inline]
251    #[must_use]
252    pub const fn is_positive(self) -> bool {
253        self.value > 0
254    }
255
256    /// Checks if the two values are [identical](https://www.w3.org/TR/xmlschema11-2/#identity).
257    #[inline]
258    #[must_use]
259    pub fn is_identical_with(self, other: Self) -> bool {
260        self == other
261    }
262
263    #[inline]
264    #[must_use]
265    pub(super) const fn as_i128(self) -> i128 {
266        self.value / DECIMAL_PART_POW
267    }
268}
269
270impl From<bool> for Decimal {
271    #[inline]
272    fn from(value: bool) -> Self {
273        Self {
274            value: i128::from(value) * DECIMAL_PART_POW,
275        }
276    }
277}
278
279impl From<i8> for Decimal {
280    #[inline]
281    fn from(value: i8) -> Self {
282        Self {
283            value: i128::from(value) * DECIMAL_PART_POW,
284        }
285    }
286}
287
288impl From<i16> for Decimal {
289    #[inline]
290    fn from(value: i16) -> Self {
291        Self {
292            value: i128::from(value) * DECIMAL_PART_POW,
293        }
294    }
295}
296
297impl From<i32> for Decimal {
298    #[inline]
299    fn from(value: i32) -> Self {
300        Self {
301            value: i128::from(value) * DECIMAL_PART_POW,
302        }
303    }
304}
305
306impl From<i64> for Decimal {
307    #[inline]
308    fn from(value: i64) -> Self {
309        Self {
310            value: i128::from(value) * DECIMAL_PART_POW,
311        }
312    }
313}
314
315impl From<u8> for Decimal {
316    #[inline]
317    fn from(value: u8) -> Self {
318        Self {
319            value: i128::from(value) * DECIMAL_PART_POW,
320        }
321    }
322}
323
324impl From<u16> for Decimal {
325    #[inline]
326    fn from(value: u16) -> Self {
327        Self {
328            value: i128::from(value) * DECIMAL_PART_POW,
329        }
330    }
331}
332
333impl From<u32> for Decimal {
334    #[inline]
335    fn from(value: u32) -> Self {
336        Self {
337            value: i128::from(value) * DECIMAL_PART_POW,
338        }
339    }
340}
341
342impl From<u64> for Decimal {
343    #[inline]
344    fn from(value: u64) -> Self {
345        Self {
346            value: i128::from(value) * DECIMAL_PART_POW,
347        }
348    }
349}
350
351impl From<Integer> for Decimal {
352    #[inline]
353    fn from(value: Integer) -> Self {
354        i64::from(value).into()
355    }
356}
357
358impl TryFrom<i128> for Decimal {
359    type Error = TooLargeForDecimalError;
360
361    #[inline]
362    fn try_from(value: i128) -> Result<Self, Self::Error> {
363        Ok(Self {
364            value: value
365                .checked_mul(DECIMAL_PART_POW)
366                .ok_or(TooLargeForDecimalError)?,
367        })
368    }
369}
370
371impl TryFrom<u128> for Decimal {
372    type Error = TooLargeForDecimalError;
373
374    #[inline]
375    fn try_from(value: u128) -> Result<Self, Self::Error> {
376        Ok(Self {
377            value: i128::try_from(value)
378                .map_err(|_| TooLargeForDecimalError)?
379                .checked_mul(DECIMAL_PART_POW)
380                .ok_or(TooLargeForDecimalError)?,
381        })
382    }
383}
384
385impl From<Boolean> for Decimal {
386    #[inline]
387    fn from(value: Boolean) -> Self {
388        bool::from(value).into()
389    }
390}
391
392impl TryFrom<Float> for Decimal {
393    type Error = TooLargeForDecimalError;
394
395    #[inline]
396    fn try_from(value: Float) -> Result<Self, Self::Error> {
397        Double::from(value).try_into()
398    }
399}
400
401impl TryFrom<Double> for Decimal {
402    type Error = TooLargeForDecimalError;
403
404    #[inline]
405    #[allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
406    fn try_from(value: Double) -> Result<Self, Self::Error> {
407        let shifted = f64::from(value) * (DECIMAL_PART_POW as f64);
408        if (i128::MIN as f64) <= shifted && shifted <= (i128::MAX as f64) {
409            Ok(Self {
410                value: shifted as i128,
411            })
412        } else {
413            Err(TooLargeForDecimalError)
414        }
415    }
416}
417
418impl From<Decimal> for Float {
419    #[inline]
420    #[allow(clippy::cast_precision_loss)]
421    fn from(value: Decimal) -> Self {
422        Double::from(value).into()
423    }
424}
425
426impl From<Decimal> for Double {
427    #[inline]
428    #[allow(clippy::cast_precision_loss)]
429    fn from(value: Decimal) -> Self {
430        let mut value = value.value;
431        let mut shift = DECIMAL_PART_POW;
432
433        // Hack to improve precision
434        if value != 0 {
435            while shift != 1 && value % 10 == 0 {
436                value /= 10;
437                shift /= 10;
438            }
439        }
440
441        ((value as f64) / (shift as f64)).into()
442    }
443}
444
445impl TryFrom<Decimal> for Integer {
446    type Error = TooLargeForIntegerError;
447
448    #[inline]
449    fn try_from(value: Decimal) -> Result<Self, Self::Error> {
450        Ok(i64::try_from(
451            value
452                .value
453                .checked_div(DECIMAL_PART_POW)
454                .ok_or(TooLargeForIntegerError)?,
455        )
456        .map_err(|_| TooLargeForIntegerError)?
457        .into())
458    }
459}
460
461impl FromStr for Decimal {
462    type Err = ParseDecimalError;
463
464    /// Parses decimals lexical mapping
465    fn from_str(input: &str) -> Result<Self, Self::Err> {
466        // (\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+)
467        let input = input.as_bytes();
468        if input.is_empty() {
469            return Err(PARSE_UNEXPECTED_END);
470        }
471
472        let (sign, mut input) = match input.first() {
473            Some(b'+') => (1_i128, &input[1..]),
474            Some(b'-') => (-1_i128, &input[1..]),
475            _ => (1, input),
476        };
477
478        let mut value = 0_i128;
479        let with_before_dot = input.first().is_some_and(u8::is_ascii_digit);
480        while let Some(c) = input.first() {
481            if c.is_ascii_digit() {
482                value = value
483                    .checked_mul(10)
484                    .ok_or(PARSE_OVERFLOW)?
485                    .checked_add(sign * i128::from(*c - b'0'))
486                    .ok_or(PARSE_OVERFLOW)?;
487                input = &input[1..];
488            } else {
489                break;
490            }
491        }
492
493        let mut exp = DECIMAL_PART_POW;
494        if let Some(c) = input.first() {
495            if *c != b'.' {
496                return Err(PARSE_UNEXPECTED_CHAR);
497            }
498            input = &input[1..];
499            if input.is_empty() && !with_before_dot {
500                // We only have a dot
501                return Err(PARSE_UNEXPECTED_END);
502            }
503            while input.last() == Some(&b'0') {
504                // Hack to avoid underflows
505                input = &input[..input.len() - 1];
506            }
507            while let Some(c) = input.first() {
508                if c.is_ascii_digit() {
509                    exp /= 10;
510                    value = value
511                        .checked_mul(10)
512                        .ok_or(PARSE_OVERFLOW)?
513                        .checked_add(sign * i128::from(*c - b'0'))
514                        .ok_or(PARSE_OVERFLOW)?;
515                    input = &input[1..];
516                } else {
517                    return Err(PARSE_UNEXPECTED_CHAR);
518                }
519            }
520            if exp == 0 {
521                // Underflow
522                return Err(PARSE_UNDERFLOW);
523            }
524        } else if !with_before_dot {
525            // It's empty
526            return Err(PARSE_UNEXPECTED_END);
527        }
528
529        Ok(Self {
530            value: value.checked_mul(exp).ok_or(PARSE_OVERFLOW)?,
531        })
532    }
533}
534
535impl fmt::Display for Decimal {
536    /// Formats the decimal following its canonical representation.
537    #[allow(clippy::cast_possible_truncation)]
538    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
539        if self.value == 0 {
540            return if let Some(width) = f.width() {
541                for _ in 0..width {
542                    f.write_char('0')?;
543                }
544                Ok(())
545            } else {
546                f.write_char('0')
547            };
548        }
549
550        let mut value = self.value;
551        if self.value.is_negative() {
552            f.write_char('-')?;
553        }
554
555        let mut digits = [b'0'; 40];
556        let mut i = 0;
557        while value != 0 {
558            digits[i] = b'0' + ((value % 10).unsigned_abs() as u8);
559            value /= 10;
560            i += 1;
561        }
562
563        let last_non_zero = i - 1;
564        let first_non_zero = digits
565            .iter()
566            .copied()
567            .enumerate()
568            .find_map(|(i, v)| if v == b'0' { None } else { Some(i) })
569            .unwrap_or(40);
570
571        let decimal_part_digits = usize::try_from(DECIMAL_PART_DIGITS).map_err(|_| fmt::Error)?;
572        if last_non_zero >= decimal_part_digits {
573            let end = if let Some(mut width) = f.width() {
574                if self.value.is_negative() {
575                    width -= 1;
576                }
577                if last_non_zero - decimal_part_digits + 1 < width {
578                    decimal_part_digits + width
579                } else {
580                    last_non_zero + 1
581                }
582            } else {
583                last_non_zero + 1
584            };
585            for c in digits[decimal_part_digits..end].iter().rev() {
586                f.write_char(char::from(*c))?;
587            }
588        } else {
589            f.write_char('0')?
590        }
591        if decimal_part_digits > first_non_zero {
592            f.write_char('.')?;
593            let start = if let Some(precision) = f.precision() {
594                if decimal_part_digits - first_non_zero > precision {
595                    decimal_part_digits - precision
596                } else {
597                    first_non_zero
598                }
599            } else {
600                first_non_zero
601            };
602            for c in digits[start..decimal_part_digits].iter().rev() {
603                f.write_char(char::from(*c))?;
604            }
605        }
606
607        Ok(())
608    }
609}
610
611/// An error when parsing a [`Decimal`].
612#[derive(Debug, thiserror::Error)]
613#[error(transparent)]
614pub struct ParseDecimalError(#[from] DecimalParseErrorKind);
615
616#[derive(Debug, Clone, thiserror::Error)]
617enum DecimalParseErrorKind {
618    #[error("Value overflow")]
619    Overflow,
620    #[error("Value underflow")]
621    Underflow,
622    #[error("Unexpected character")]
623    UnexpectedChar,
624    #[error("Unexpected end of string")]
625    UnexpectedEnd,
626}
627
628const PARSE_OVERFLOW: ParseDecimalError = ParseDecimalError(DecimalParseErrorKind::Overflow);
629const PARSE_UNDERFLOW: ParseDecimalError = ParseDecimalError(DecimalParseErrorKind::Underflow);
630const PARSE_UNEXPECTED_CHAR: ParseDecimalError =
631    ParseDecimalError(DecimalParseErrorKind::UnexpectedChar);
632const PARSE_UNEXPECTED_END: ParseDecimalError =
633    ParseDecimalError(DecimalParseErrorKind::UnexpectedEnd);
634
635impl From<TooLargeForDecimalError> for ParseDecimalError {
636    fn from(_: TooLargeForDecimalError) -> Self {
637        Self(DecimalParseErrorKind::Overflow)
638    }
639}
640
641/// The input is too large to fit into a [`Decimal`].
642///
643/// Matches XPath [`FOCA0001` error](https://www.w3.org/TR/xpath-functions-31/#ERRFOCA0001).
644#[derive(Debug, Clone, Copy, thiserror::Error)]
645#[error("Value too large for xsd:decimal internal representation")]
646pub struct TooLargeForDecimalError;
647
648#[cfg(test)]
649#[allow(clippy::panic_in_result_fn)]
650mod tests {
651    use super::*;
652
653    #[test]
654    fn new() -> Result<(), ParseDecimalError> {
655        assert_eq!(Decimal::new(1, 0)?.to_string(), "1");
656        assert_eq!(Decimal::new(1, 1)?.to_string(), "0.1");
657        assert_eq!(Decimal::new(10, 0)?.to_string(), "10");
658        assert_eq!(Decimal::new(10, 1)?.to_string(), "1");
659        assert_eq!(Decimal::new(10, 2)?.to_string(), "0.1");
660        Ok(())
661    }
662
663    #[test]
664    fn from_str() -> Result<(), ParseDecimalError> {
665        Decimal::from_str("").unwrap_err();
666        Decimal::from_str("+").unwrap_err();
667        Decimal::from_str("-").unwrap_err();
668        Decimal::from_str(".").unwrap_err();
669        Decimal::from_str("+.").unwrap_err();
670        Decimal::from_str("-.").unwrap_err();
671        Decimal::from_str("a").unwrap_err();
672        Decimal::from_str(".a").unwrap_err();
673        assert_eq!(Decimal::from_str("210")?.to_string(), "210");
674        assert_eq!(Decimal::from_str("1000")?.to_string(), "1000");
675        assert_eq!(Decimal::from_str("-1.23")?.to_string(), "-1.23");
676        assert_eq!(
677            Decimal::from_str("12678967.543233")?.to_string(),
678            "12678967.543233"
679        );
680        assert_eq!(Decimal::from_str("+100000.00")?.to_string(), "100000");
681        assert_eq!(Decimal::from_str("0.1220")?.to_string(), "0.122");
682        assert_eq!(Decimal::from_str(".12200")?.to_string(), "0.122");
683        assert_eq!(Decimal::from_str("1.")?.to_string(), "1");
684        assert_eq!(Decimal::from_str("1.0")?.to_string(), "1");
685        assert_eq!(Decimal::from_str("01.0")?.to_string(), "1");
686        assert_eq!(Decimal::from_str("0")?.to_string(), "0");
687        assert_eq!(Decimal::from_str("-0")?.to_string(), "0");
688        assert_eq!(Decimal::from_str(&Decimal::MAX.to_string())?, Decimal::MAX);
689        assert_eq!(Decimal::from_str(&Decimal::MIN.to_string())?, Decimal::MIN);
690        Decimal::from_str("0.0000000000000000001").unwrap_err();
691        Decimal::from_str("1000000000000000000000").unwrap_err();
692        assert_eq!(
693            Decimal::from_str("0.100000000000000000000000000").unwrap(),
694            Decimal::from_str("0.1").unwrap()
695        );
696        Ok(())
697    }
698
699    #[test]
700    fn format() {
701        assert_eq!(format!("{}", Decimal::from(0)), "0");
702        assert_eq!(format!("{}", Decimal::from(1)), "1");
703        assert_eq!(format!("{}", Decimal::from(10)), "10");
704        assert_eq!(format!("{}", Decimal::from(100)), "100");
705        assert_eq!(format!("{}", Decimal::from(-1)), "-1");
706        assert_eq!(format!("{}", Decimal::from(-10)), "-10");
707
708        assert_eq!(format!("{:02}", Decimal::from(0)), "00");
709        assert_eq!(format!("{:02}", Decimal::from(1)), "01");
710        assert_eq!(format!("{:02}", Decimal::from(10)), "10");
711        assert_eq!(format!("{:02}", Decimal::from(100)), "100");
712        assert_eq!(format!("{:02}", Decimal::from(-1)), "-1");
713        assert_eq!(format!("{:02}", Decimal::from(-10)), "-10");
714    }
715
716    #[test]
717    fn add() {
718        assert!(Decimal::MIN.checked_add(Decimal::STEP).is_some());
719        assert!(Decimal::MAX.checked_add(Decimal::STEP).is_none());
720        assert_eq!(
721            Decimal::MAX.checked_add(Decimal::MIN),
722            Decimal::STEP.checked_neg()
723        );
724    }
725
726    #[test]
727    fn sub() {
728        assert!(Decimal::MIN.checked_sub(Decimal::STEP).is_none());
729        assert!(Decimal::MAX.checked_sub(Decimal::STEP).is_some());
730    }
731
732    #[test]
733    fn mul() -> Result<(), ParseDecimalError> {
734        assert_eq!(Decimal::from(1).checked_mul(-1), Some(Decimal::from(-1)));
735        assert_eq!(
736            Decimal::from(1000).checked_mul(1000),
737            Some(Decimal::from(1_000_000))
738        );
739        assert_eq!(
740            Decimal::from_str("0.1")?.checked_mul(Decimal::from_str("0.01")?),
741            Some(Decimal::from_str("0.001")?)
742        );
743        assert_eq!(Decimal::from(0).checked_mul(1), Some(Decimal::from(0)));
744        assert_eq!(Decimal::from(1).checked_mul(0), Some(Decimal::from(0)));
745        assert_eq!(Decimal::MAX.checked_mul(1), Some(Decimal::MAX));
746        assert_eq!(Decimal::MIN.checked_mul(1), Some(Decimal::MIN));
747        assert_eq!(
748            Decimal::from(1).checked_mul(Decimal::MAX),
749            Some(Decimal::MAX)
750        );
751        assert_eq!(
752            Decimal::from(1).checked_mul(Decimal::MIN),
753            Some(Decimal::MIN)
754        );
755        assert_eq!(
756            Decimal::MAX.checked_mul(-1),
757            Some(Decimal::MIN.checked_add(Decimal::STEP).unwrap())
758        );
759        assert_eq!(Decimal::MIN.checked_mul(-1), None);
760        assert_eq!(
761            Decimal::MIN
762                .checked_add(Decimal::STEP)
763                .unwrap()
764                .checked_mul(-1),
765            Some(Decimal::MAX)
766        );
767        Ok(())
768    }
769
770    #[test]
771    fn div() -> Result<(), ParseDecimalError> {
772        assert_eq!(Decimal::from(1).checked_div(1), Some(Decimal::from(1)));
773        assert_eq!(Decimal::from(100).checked_div(10), Some(Decimal::from(10)));
774        assert_eq!(
775            Decimal::from(10).checked_div(100),
776            Some(Decimal::from_str("0.1")?)
777        );
778        assert_eq!(Decimal::from(1).checked_div(0), None);
779        assert_eq!(Decimal::from(0).checked_div(1), Some(Decimal::from(0)));
780        assert_eq!(Decimal::MAX.checked_div(1), Some(Decimal::MAX));
781        assert_eq!(Decimal::MIN.checked_div(1), Some(Decimal::MIN));
782        assert_eq!(
783            Decimal::MAX.checked_div(-1),
784            Some(Decimal::MIN.checked_add(Decimal::STEP).unwrap())
785        );
786        assert_eq!(Decimal::MIN.checked_div(-1), None);
787        assert_eq!(
788            Decimal::MIN
789                .checked_add(Decimal::STEP)
790                .unwrap()
791                .checked_div(-1),
792            Some(Decimal::MAX)
793        );
794        Ok(())
795    }
796
797    #[test]
798    fn rem() -> Result<(), ParseDecimalError> {
799        assert_eq!(Decimal::from(10).checked_rem(3), Some(Decimal::from(1)));
800        assert_eq!(Decimal::from(6).checked_rem(-2), Some(Decimal::from(0)));
801        assert_eq!(
802            Decimal::from_str("4.5")?.checked_rem(Decimal::from_str("1.2")?),
803            Some(Decimal::from_str("0.9")?)
804        );
805        assert_eq!(Decimal::from(1).checked_rem(0), None);
806        assert_eq!(
807            Decimal::MAX.checked_rem(1),
808            Some(Decimal::from_str("0.687303715884105727")?)
809        );
810        assert_eq!(
811            Decimal::MIN.checked_rem(1),
812            Some(Decimal::from_str("-0.687303715884105728")?)
813        );
814        assert_eq!(
815            Decimal::MAX.checked_rem(Decimal::STEP),
816            Some(Decimal::default())
817        );
818        assert_eq!(
819            Decimal::MIN.checked_rem(Decimal::STEP),
820            Some(Decimal::default())
821        );
822        assert_eq!(
823            Decimal::MAX.checked_rem(Decimal::MAX),
824            Some(Decimal::default())
825        );
826        assert_eq!(
827            Decimal::MIN.checked_rem(Decimal::MIN),
828            Some(Decimal::default())
829        );
830        Ok(())
831    }
832
833    #[test]
834    fn round() -> Result<(), ParseDecimalError> {
835        assert_eq!(Decimal::from(10).checked_round(), Some(Decimal::from(10)));
836        assert_eq!(Decimal::from(-10).checked_round(), Some(Decimal::from(-10)));
837        assert_eq!(
838            Decimal::from(i64::MIN).checked_round(),
839            Some(Decimal::from(i64::MIN))
840        );
841        assert_eq!(
842            Decimal::from(i64::MAX).checked_round(),
843            Some(Decimal::from(i64::MAX))
844        );
845        assert_eq!(
846            Decimal::from_str("2.5")?.checked_round(),
847            Some(Decimal::from(3))
848        );
849        assert_eq!(
850            Decimal::from_str("2.4999")?.checked_round(),
851            Some(Decimal::from(2))
852        );
853        assert_eq!(
854            Decimal::from_str("-2.5")?.checked_round(),
855            Some(Decimal::from(-2))
856        );
857        assert_eq!(Decimal::MAX.checked_round(), None);
858        assert_eq!(
859            Decimal::MAX
860                .checked_sub(Decimal::from_str("0.5")?)
861                .unwrap()
862                .checked_round(),
863            Some(Decimal::from_str("170141183460469231731")?)
864        );
865        assert_eq!(Decimal::MIN.checked_round(), None);
866        assert_eq!(
867            Decimal::MIN
868                .checked_add(Decimal::from_str("0.5")?)
869                .unwrap()
870                .checked_round(),
871            Some(Decimal::from_str("-170141183460469231731")?)
872        );
873        Ok(())
874    }
875
876    #[test]
877    fn ceil() -> Result<(), ParseDecimalError> {
878        assert_eq!(Decimal::from(10).checked_ceil(), Some(Decimal::from(10)));
879        assert_eq!(Decimal::from(-10).checked_ceil(), Some(Decimal::from(-10)));
880        assert_eq!(
881            Decimal::from_str("10.5")?.checked_ceil(),
882            Some(Decimal::from(11))
883        );
884        assert_eq!(
885            Decimal::from_str("-10.5")?.checked_ceil(),
886            Some(Decimal::from(-10))
887        );
888        assert_eq!(
889            Decimal::from(i64::MIN).checked_ceil(),
890            Some(Decimal::from(i64::MIN))
891        );
892        assert_eq!(
893            Decimal::from(i64::MAX).checked_ceil(),
894            Some(Decimal::from(i64::MAX))
895        );
896        assert_eq!(Decimal::MAX.checked_ceil(), None);
897        assert_eq!(
898            Decimal::MAX
899                .checked_sub(Decimal::from(1))
900                .unwrap()
901                .checked_ceil(),
902            Some(Decimal::from_str("170141183460469231731")?)
903        );
904        assert_eq!(
905            Decimal::MIN.checked_ceil(),
906            Some(Decimal::from_str("-170141183460469231731")?)
907        );
908        Ok(())
909    }
910
911    #[test]
912    fn floor() -> Result<(), ParseDecimalError> {
913        assert_eq!(Decimal::from(10).checked_floor(), Some(Decimal::from(10)));
914        assert_eq!(Decimal::from(-10).checked_floor(), Some(Decimal::from(-10)));
915        assert_eq!(
916            Decimal::from_str("10.5")?.checked_floor(),
917            Some(Decimal::from(10))
918        );
919        assert_eq!(
920            Decimal::from_str("-10.5")?.checked_floor(),
921            Some(Decimal::from(-11))
922        );
923        assert_eq!(
924            Decimal::from(i64::MIN).checked_floor(),
925            Some(Decimal::from(i64::MIN))
926        );
927        assert_eq!(
928            Decimal::from(i64::MAX).checked_floor(),
929            Some(Decimal::from(i64::MAX))
930        );
931        assert_eq!(
932            Decimal::MAX.checked_floor(),
933            Some(Decimal::from_str("170141183460469231731")?)
934        );
935        assert_eq!(Decimal::MIN.checked_floor(), None);
936        assert_eq!(
937            Decimal::MIN
938                .checked_add(Decimal::from_str("1")?)
939                .unwrap()
940                .checked_floor(),
941            Some(Decimal::from_str("-170141183460469231731")?)
942        );
943        Ok(())
944    }
945
946    #[test]
947    fn to_be_bytes() -> Result<(), ParseDecimalError> {
948        assert_eq!(
949            Decimal::from_be_bytes(Decimal::MIN.to_be_bytes()),
950            Decimal::MIN
951        );
952        assert_eq!(
953            Decimal::from_be_bytes(Decimal::MAX.to_be_bytes()),
954            Decimal::MAX
955        );
956        assert_eq!(
957            Decimal::from_be_bytes(Decimal::from(i64::MIN).to_be_bytes()),
958            Decimal::from(i64::MIN)
959        );
960        assert_eq!(
961            Decimal::from_be_bytes(Decimal::from(i64::MAX).to_be_bytes()),
962            Decimal::from(i64::MAX)
963        );
964        assert_eq!(
965            Decimal::from_be_bytes(Decimal::from(0).to_be_bytes()),
966            Decimal::from(0)
967        );
968        assert_eq!(
969            Decimal::from_be_bytes(Decimal::from(0).to_be_bytes()),
970            Decimal::from(0)
971        );
972        assert_eq!(
973            Decimal::from_be_bytes(Decimal::from_str("0.01")?.to_be_bytes()),
974            Decimal::from_str("0.01")?
975        );
976        Ok(())
977    }
978
979    #[test]
980    fn from_bool() {
981        assert_eq!(Decimal::from(false), Decimal::from(0_u8));
982        assert_eq!(Decimal::from(true), Decimal::from(1_u8));
983    }
984
985    #[test]
986    fn from_float() -> Result<(), ParseDecimalError> {
987        assert_eq!(
988            Decimal::try_from(Float::from(0.)).ok(),
989            Some(Decimal::from(0))
990        );
991        assert_eq!(
992            Decimal::try_from(Float::from(-0.)).ok(),
993            Some(Decimal::from(0))
994        );
995        assert_eq!(
996            Decimal::try_from(Float::from(-123.5)).ok(),
997            Some(Decimal::from_str("-123.5")?)
998        );
999        Decimal::try_from(Float::from(f32::NAN)).unwrap_err();
1000        Decimal::try_from(Float::from(f32::INFINITY)).unwrap_err();
1001        Decimal::try_from(Float::from(f32::NEG_INFINITY)).unwrap_err();
1002        Decimal::try_from(Float::from(f32::MIN)).unwrap_err();
1003        Decimal::try_from(Float::from(f32::MAX)).unwrap_err();
1004        assert!(
1005            Decimal::try_from(Float::from(1_672_507_300_000.))
1006                .unwrap()
1007                .checked_sub(Decimal::from(1_672_507_293_696_i64))
1008                .unwrap()
1009                .checked_abs()
1010                .unwrap()
1011                < Decimal::from(1)
1012        );
1013        Ok(())
1014    }
1015
1016    #[test]
1017    fn from_double() -> Result<(), ParseDecimalError> {
1018        assert_eq!(
1019            Decimal::try_from(Double::from(0.)).ok(),
1020            Some(Decimal::from(0))
1021        );
1022        assert_eq!(
1023            Decimal::try_from(Double::from(-0.)).ok(),
1024            Some(Decimal::from(0))
1025        );
1026        assert_eq!(
1027            Decimal::try_from(Double::from(-123.1)).ok(),
1028            Some(Decimal::from_str("-123.1")?)
1029        );
1030        assert!(
1031            Decimal::try_from(Double::from(1_672_507_302_466.))
1032                .unwrap()
1033                .checked_sub(Decimal::from(1_672_507_302_466_i64))
1034                .unwrap()
1035                .checked_abs()
1036                .unwrap()
1037                < Decimal::from(1)
1038        );
1039        Decimal::try_from(Double::from(f64::NAN)).unwrap_err();
1040        Decimal::try_from(Double::from(f64::INFINITY)).unwrap_err();
1041        Decimal::try_from(Double::from(f64::NEG_INFINITY)).unwrap_err();
1042        Decimal::try_from(Double::from(f64::MIN)).unwrap_err();
1043        Decimal::try_from(Double::from(f64::MAX)).unwrap_err();
1044        Ok(())
1045    }
1046
1047    #[test]
1048    fn to_float() -> Result<(), ParseDecimalError> {
1049        assert_eq!(Float::from(Decimal::from(0)), Float::from(0.));
1050        assert_eq!(Float::from(Decimal::from(1)), Float::from(1.));
1051        assert_eq!(Float::from(Decimal::from(10)), Float::from(10.));
1052        assert_eq!(Float::from(Decimal::from_str("0.1")?), Float::from(0.1));
1053        assert!((Float::from(Decimal::MAX) - Float::from(1.701_412e20)).abs() < Float::from(1.));
1054        assert!((Float::from(Decimal::MIN) - Float::from(-1.701_412e20)).abs() < Float::from(1.));
1055        Ok(())
1056    }
1057
1058    #[test]
1059    fn to_double() -> Result<(), ParseDecimalError> {
1060        assert_eq!(Double::from(Decimal::from(0)), Double::from(0.));
1061        assert_eq!(Double::from(Decimal::from(1)), Double::from(1.));
1062        assert_eq!(Double::from(Decimal::from(10)), Double::from(10.));
1063        assert!(
1064            Double::from(Decimal::from_str("0.1")?) - Double::from(0.1)
1065                < Double::from(f64::from(f32::EPSILON))
1066        );
1067        assert!(
1068            (Double::from(Decimal::MAX) - Double::from(1.701_411_834_604_692_4e20)).abs()
1069                < Double::from(1.)
1070        );
1071        assert!(
1072            (Double::from(Decimal::MIN) - Double::from(-1.701_411_834_604_692_4e20)).abs()
1073                < Double::from(1.)
1074        );
1075        Ok(())
1076    }
1077
1078    #[test]
1079    fn minimally_conformant() -> Result<(), ParseDecimalError> {
1080        // All minimally conforming processors must support decimal values whose absolute value can be expressed as i / 10^k,
1081        // where i and k are nonnegative integers such that i < 10^16 and k ≤ 16 (i.e., those expressible with sixteen total digits).
1082        assert_eq!(
1083            Decimal::from_str("1234567890123456")?.to_string(),
1084            "1234567890123456"
1085        );
1086        assert_eq!(
1087            Decimal::from_str("-1234567890123456")?.to_string(),
1088            "-1234567890123456"
1089        );
1090        assert_eq!(
1091            Decimal::from_str("0.1234567890123456")?.to_string(),
1092            "0.1234567890123456"
1093        );
1094        assert_eq!(
1095            Decimal::from_str("-0.1234567890123456")?.to_string(),
1096            "-0.1234567890123456"
1097        );
1098        Ok(())
1099    }
1100}