Skip to main content

ferroui_base/utilities/
decimal.rs

1//! A counterpart of .NET's `System.Decimal`: an exact base-10 number with a
2//! 96-bit integer mantissa, a sign and a scale (the number of digits after
3//! the decimal point) of 0 to 28. The arithmetic is that of the
4//! `rust_decimal` crate; this type gives it the members, the text forms and
5//! the failure behaviour of the managed type.
6//!
7//! Only the members the controls use exist: construction and conversion
8//! from integers, addition, subtraction, division, comparison, and the
9//! formatting and parsing of [`number_format`](super::number_format).
10//!
11//! The scale is part of the value's representation, as in .NET: `1.10` and
12//! `1.1` are equal, hash alike and order alike, but print as `1.10` and
13//! `1.1`. Sums and differences take the larger scale. Results outside the
14//! value range panic (the overflow exception of the managed runtime); the
15//! `checked_*` forms return `None` instead.
16
17use super::number_format::{self, NumberParseError};
18use super::{NumberFormatInfo, NumberStyles};
19use crate::data::converters::composite_format::FormatError;
20use crate::data::core::ValueTypes;
21use std::fmt;
22use std::ops::{Add, AddAssign, Div, Neg, Sub, SubAssign};
23use std::str::FromStr;
24
25/// The largest mantissa: 2^96 - 1.
26const MAX_MANTISSA: u128 = (1u128 << 96) - 1;
27
28/// The largest scale.
29const MAX_SCALE: u8 = 28;
30
31const OVERFLOW: &str = "Value was either too large or too small for a Decimal.";
32
33/// The powers of ten from 10^0 to 10^28 as doubles.
34const DOUBLE_POWERS_10: [f64; 29] = [
35    1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
36    1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28,
37];
38
39/// An exact decimal number.
40#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
41pub struct Decimal(rust_decimal::Decimal);
42
43impl Decimal {
44    /// The number zero.
45    pub const ZERO: Decimal = Decimal(rust_decimal::Decimal::ZERO);
46
47    /// The number one.
48    pub const ONE: Decimal = Decimal(rust_decimal::Decimal::ONE);
49
50    /// The largest value: 79,228,162,514,264,337,593,543,950,335.
51    pub const MAX_VALUE: Decimal = Decimal(rust_decimal::Decimal::MAX);
52
53    /// The smallest value: -79,228,162,514,264,337,593,543,950,335.
54    pub const MIN_VALUE: Decimal = Decimal(rust_decimal::Decimal::MIN);
55
56    /// A decimal of `mantissa / 10^scale`, negated when `negative`.
57    ///
58    /// Panics when the mantissa has more than 96 bits or the scale is
59    /// greater than 28.
60    pub fn from_parts(mantissa: u128, scale: u8, negative: bool) -> Decimal {
61        assert!(mantissa <= MAX_MANTISSA, "The mantissa of a Decimal has at most 96 bits.");
62        assert!(scale <= MAX_SCALE, "Decimal's scale value must be between 0 and 28, inclusive.");
63        let mut value = rust_decimal::Decimal::from_i128_with_scale(mantissa as i128, u32::from(scale));
64        value.set_sign_negative(negative);
65        Decimal(value)
66    }
67
68    /// The 96-bit integer that, divided by `10^scale`, is the magnitude.
69    #[inline]
70    pub fn mantissa(&self) -> u128 {
71        self.0.mantissa().unsigned_abs()
72    }
73
74    /// The number of digits after the decimal point (0 to 28).
75    #[inline]
76    pub fn scale(&self) -> u8 {
77        self.0.scale() as u8
78    }
79
80    /// Whether the sign bit is set. Negative zero has it set and is equal to
81    /// zero.
82    #[inline]
83    pub fn is_sign_negative(&self) -> bool {
84        self.0.is_sign_negative()
85    }
86
87    /// Whether the value is zero (of any scale and sign).
88    #[inline]
89    pub fn is_zero(&self) -> bool {
90        self.0.is_zero()
91    }
92
93    /// A sum or difference at the scale of the managed type: the larger of
94    /// the scales of the operands or, when the digits do not fit at that
95    /// scale, the largest scale at which they do.
96    fn at_scale_of(result: rust_decimal::Decimal, a: Decimal, b: Decimal) -> Decimal {
97        let scale = u32::from(a.scale().max(b.scale()));
98        let mut result_scale = result.scale();
99        if result_scale >= scale {
100            return Decimal(result);
101        }
102
103        // Trailing zeros are added while the mantissa stays within 96 bits.
104        let mut mantissa = result.mantissa().unsigned_abs();
105        while result_scale < scale {
106            match mantissa.checked_mul(10) {
107                Some(next) if next <= MAX_MANTISSA => {
108                    mantissa = next;
109                    result_scale += 1;
110                }
111                _ => break,
112            }
113        }
114        let mut rescaled = rust_decimal::Decimal::from_i128_with_scale(mantissa as i128, result_scale);
115        rescaled.set_sign_negative(result.is_sign_negative());
116        Decimal(rescaled)
117    }
118
119    /// The larger of two values (C# `Math.Max`): of two equal values the
120    /// first, which matters for their scales.
121    #[inline]
122    pub fn max(self, other: Decimal) -> Decimal {
123        if self >= other {
124            self
125        } else {
126            other
127        }
128    }
129
130    /// The smaller of two values (C# `Math.Min`): of two equal values the
131    /// second, which matters for their scales.
132    #[inline]
133    pub fn min(self, other: Decimal) -> Decimal {
134        if self < other {
135            self
136        } else {
137            other
138        }
139    }
140
141    /// The sum, or `None` when it is out of range.
142    #[inline]
143    pub fn checked_add(self, other: Decimal) -> Option<Decimal> {
144        self.0.checked_add(other.0).map(|result| Self::at_scale_of(result, self, other))
145    }
146
147    /// The difference, or `None` when it is out of range.
148    #[inline]
149    pub fn checked_sub(self, other: Decimal) -> Option<Decimal> {
150        self.0.checked_sub(other.0).map(|result| Self::at_scale_of(result, self, other))
151    }
152
153    /// The quotient, or `None` when it is out of range. Panics when `other`
154    /// is zero.
155    pub fn checked_div(self, other: Decimal) -> Option<Decimal> {
156        assert!(!other.is_zero(), "Attempted to divide by zero.");
157        let mut quotient = self.0.checked_div(other.0)?;
158        // The scale of the managed type: the difference of the scales when
159        // the mantissas divide evenly, else the fewest digits that hold the
160        // quotient.
161        if self.mantissa() % other.mantissa() == 0 {
162            quotient.rescale(u32::from(self.scale().saturating_sub(other.scale())));
163            Some(Decimal(quotient))
164        } else {
165            Some(Decimal(quotient.normalize()))
166        }
167    }
168
169    /// The nearest double-precision number (the explicit conversion of the
170    /// managed type to a double).
171    pub fn to_f64(&self) -> f64 {
172        // As the managed type converts: the 96-bit mantissa as a double,
173        // divided by the power of ten of the scale.
174        const TWO_TO_64: f64 = 1.8446744073709552e+019;
175
176        let mantissa = self.mantissa();
177        let low64 = mantissa as u64;
178        let high = (mantissa >> 64) as u32;
179        let dbl = (low64 as f64 + f64::from(high) * TWO_TO_64) / DOUBLE_POWERS_10[usize::from(self.scale())];
180
181        if self.is_sign_negative() {
182            -dbl
183        } else {
184            dbl
185        }
186    }
187
188    /// The nearest single-precision number (the explicit conversion of the
189    /// managed type to a float): the double of the value, narrowed.
190    #[inline]
191    pub fn to_f32(&self) -> f32 {
192        self.to_f64() as f32
193    }
194
195    /// The value rounded to an integer, midpoints to even (`Decimal.Round`
196    /// with no decimals, which the conversions of the managed runtime to the
197    /// integer types apply before their range check).
198    pub fn round_to_integer(&self) -> i128 {
199        let mantissa = self.mantissa();
200        let divisor = 10u128.pow(u32::from(self.scale()));
201        let mut integer = mantissa / divisor;
202        let remainder = mantissa % divisor;
203        // The divisor is even whenever there is a remainder (a scale of zero has none).
204        let half = divisor / 2;
205        if remainder > half || (remainder == half && remainder != 0 && integer % 2 == 1) {
206            integer += 1;
207        }
208
209        // At most 2^96, so it fits.
210        if self.is_sign_negative() {
211            -(integer as i128)
212        } else {
213            integer as i128
214        }
215    }
216
217    /// The decimal of a double-precision number, rounded to 15 significant
218    /// digits (the explicit conversion of a double to the managed type).
219    ///
220    /// Panics when the number is not finite or is outside the value range.
221    pub fn from_f64(value: f64) -> Decimal {
222        Self::try_from_f64(value).unwrap_or_else(|| panic!("{OVERFLOW}"))
223    }
224
225    /// The decimal of a single-precision number, rounded to 7 significant
226    /// digits (the explicit conversion of a float to the managed type).
227    ///
228    /// Panics when the number is not finite or is outside the value range.
229    pub fn from_f32(value: f32) -> Decimal {
230        Self::try_from_f32(value).unwrap_or_else(|| panic!("{OVERFLOW}"))
231    }
232
233    /// [`from_f32`](Self::from_f32), with `None` where it panics.
234    pub fn try_from_f32(value: f32) -> Option<Decimal> {
235        // As for a double: an exponent of -94 could just barely reach 0.5,
236        // smaller exponents always round to zero.
237        const SNG_BIAS: i32 = 126;
238        let exp = ((value.to_bits() >> 23) & 0xFF) as i32 - SNG_BIAS;
239        if exp < -94 {
240            return Some(Decimal::ZERO);
241        }
242
243        // Not a number and the infinities have the largest exponent.
244        if exp > 96 {
245            return None;
246        }
247
248        let negative = value < 0.0;
249        let mut dbl = f64::from(value.abs());
250
251        // Round the value to a 7-digit integer: a float has only 7 digits of
252        // precision, and the digits beyond are kept out of the decimal.
253        let mut power = 6 - ((exp * 19728) >> 16);
254        // power is between -22 and 35
255
256        if power >= 0 {
257            // Less than 7 digits: scale the value up.
258            if power > i32::from(MAX_SCALE) {
259                power = i32::from(MAX_SCALE);
260            }
261
262            dbl *= DOUBLE_POWERS_10[power as usize];
263        } else if power != -1 || dbl >= 1E7 {
264            dbl /= DOUBLE_POWERS_10[(-power) as usize];
265        } else {
266            power = 0; // didn't scale it
267        }
268
269        if dbl < 1E6 && power < i32::from(MAX_SCALE) {
270            dbl *= 10.0;
271            power += 1;
272        }
273
274        // Round to an integer, midpoints to even.
275        let mut mant = dbl.round_ties_even() as u64;
276        if mant == 0 {
277            return Some(Decimal::ZERO);
278        }
279
280        if power < 0 {
281            // Add -power factors of 10, -power <= (29 - 7) = 22.
282            Some(Decimal::from_parts(u128::from(mant) * 10u128.pow((-power) as u32), 0, negative))
283        } else {
284            // Factor out powers of 10 to reduce the scale, if possible. The
285            // most that could be factored out is 6: the number has 7 digits
286            // and the most significant one is not zero.
287            let mut lmax = power.min(6);
288            while lmax > 0 && mant % 10 == 0 {
289                mant /= 10;
290                power -= 1;
291                lmax -= 1;
292            }
293
294            Some(Decimal::from_parts(u128::from(mant), power as u8, negative))
295        }
296    }
297
298    /// [`from_f64`](Self::from_f64), with `None` where it panics.
299    pub fn try_from_f64(value: f64) -> Option<Decimal> {
300        // The most the value can be scaled by is 10^28, which is just
301        // slightly more than 2^93. So a double with an exponent of -94 could
302        // just barely reach 0.5, but smaller exponents always round to zero.
303        const DBL_BIAS: i32 = 1022;
304        let exp = ((value.to_bits() >> 52) & 0x7FF) as i32 - DBL_BIAS;
305        if exp < -94 {
306            return Some(Decimal::ZERO);
307        }
308
309        // Not a number and the infinities have the largest exponent.
310        if exp > 96 {
311            return None;
312        }
313
314        let negative = value < 0.0;
315        let mut dbl = value.abs();
316
317        // Round the value to a 15-digit integer: a double has only 15 digits
318        // of precision, and the digits beyond are kept out of the decimal.
319        //
320        // The largest power of ten the value could have is the exponent
321        // multiplied by log10(2); with scaled integer multiplication,
322        // log10(2) * 2^16 = .30103 * 65536 = 19728.3.
323        let mut power = 14 - ((exp * 19728) >> 16);
324        // power is between -14 and 43
325
326        if power >= 0 {
327            // Less than 15 digits: scale the value up.
328            if power > i32::from(MAX_SCALE) {
329                power = i32::from(MAX_SCALE);
330            }
331
332            dbl *= DOUBLE_POWERS_10[power as usize];
333        } else if power != -1 || dbl >= 1E15 {
334            dbl /= DOUBLE_POWERS_10[(-power) as usize];
335        } else {
336            power = 0; // didn't scale it
337        }
338
339        if dbl < 1E14 && power < i32::from(MAX_SCALE) {
340            dbl *= 10.0;
341            power += 1;
342        }
343
344        // Round to an integer, midpoints to even.
345        let mut mant = dbl.round_ties_even() as u64;
346        if mant == 0 {
347            return Some(Decimal::ZERO);
348        }
349
350        if power < 0 {
351            // Add -power factors of 10, -power <= (29 - 15) = 14.
352            Some(Decimal::from_parts(u128::from(mant) * 10u128.pow((-power) as u32), 0, negative))
353        } else {
354            // Factor out powers of 10 to reduce the scale, if possible. The
355            // most that could be factored out is 14: the number has 15
356            // digits and the most significant one is not zero. The scale is
357            // never negative, so no more than the power that made the
358            // integer can be factored out either.
359            let mut lmax = power.min(14);
360            while lmax > 0 && mant % 10 == 0 {
361                mant /= 10;
362                power -= 1;
363                lmax -= 1;
364            }
365
366            Some(Decimal::from_parts(u128::from(mant), power as u8, negative))
367        }
368    }
369
370    /// Parses text of the invariant culture with the `Number` styles:
371    /// leading and trailing white space, a leading or trailing sign, a
372    /// decimal point and group separators.
373    pub fn parse(s: &str) -> Result<Decimal, NumberParseError> {
374        number_format::parse_decimal(s, NumberStyles::NUMBER, &NumberFormatInfo::invariant_info())
375    }
376
377    /// [`Decimal::parse`] without the reason of a failure.
378    #[inline]
379    pub fn try_parse(s: &str) -> Option<Decimal> {
380        Decimal::parse(s).ok()
381    }
382
383    /// Parses text with the given styles and format information (C#
384    /// `decimal.Parse(string, NumberStyles, IFormatProvider)`). Without a
385    /// provider the format information of the current culture is used.
386    ///
387    /// Styles that are not defined, and the hexadecimal and binary styles,
388    /// are an error ([`NumberParseError::InvalidStyle`]).
389    pub fn parse_with(
390        s: &str,
391        styles: NumberStyles,
392        provider: Option<&NumberFormatInfo>,
393    ) -> Result<Decimal, NumberParseError> {
394        match provider {
395            Some(info) => number_format::parse_decimal(s, styles, info),
396            None => number_format::parse_decimal(s, styles, &NumberFormatInfo::current_info()),
397        }
398    }
399
400    /// [`Decimal::parse_with`] without the reason of a failure.
401    #[inline]
402    pub fn try_parse_with(s: &str, styles: NumberStyles, provider: Option<&NumberFormatInfo>) -> Option<Decimal> {
403        Decimal::parse_with(s, styles, provider).ok()
404    }
405
406    /// Formats the value with a standard or custom numeric format string
407    /// (C# `ToString(string, IFormatProvider)`). Without a provider the
408    /// format information of the current culture is used.
409    pub fn to_string_with(&self, format: &str, provider: Option<&NumberFormatInfo>) -> Result<String, FormatError> {
410        match provider {
411            Some(info) => number_format::format_decimal(*self, format, info),
412            None => number_format::format_decimal(*self, format, &NumberFormatInfo::current_info()),
413        }
414    }
415
416    /// Formats the value in the general format with the given format
417    /// information (C# `ToString(IFormatProvider)`).
418    pub fn to_string_provider(&self, provider: &NumberFormatInfo) -> String {
419        number_format::format_decimal(*self, "", provider).unwrap_or_default()
420    }
421
422    /// Makes the type known to the untyped value conversions: its text
423    /// form, its nullable form and the conversions from text and from and
424    /// to the other numbers.
425    pub(crate) fn register_value_type() {
426        ValueTypes::register_display::<Decimal>();
427        ValueTypes::register_nullable::<Decimal>();
428        ValueTypes::register_parse::<Decimal>(|s| Decimal::parse(s).ok());
429        ValueTypes::register_conversion::<String, Option<Decimal>>(|s| Decimal::parse(s).ok().map(Some));
430        ValueTypes::register_conversion::<&'static str, Decimal>(|s| Decimal::parse(s).ok());
431        ValueTypes::register_conversion::<&'static str, Option<Decimal>>(|s| Decimal::parse(s).ok().map(Some));
432
433        macro_rules! integers {
434            ($($ty:ty),*) => {
435                $(
436                    ValueTypes::register_conversion::<$ty, Decimal>(|v| Some(Decimal::from(*v)));
437                    ValueTypes::register_conversion::<$ty, Option<Decimal>>(|v| Some(Some(Decimal::from(*v))));
438                )*
439            };
440        }
441        integers!(i8, i16, i32, i64, u8, u16, u32, u64, isize, usize);
442
443        // The floating point numbers convert when they are in the range of
444        // the type (`Convert.ToDecimal` fails for the others, and for the
445        // infinities and not-a-number).
446        ValueTypes::register_conversion::<f64, Decimal>(|v| Decimal::try_from_f64(*v));
447        ValueTypes::register_conversion::<f64, Option<Decimal>>(|v| Decimal::try_from_f64(*v).map(Some));
448        ValueTypes::register_conversion::<f32, Decimal>(|v| Decimal::try_from_f32(*v));
449        ValueTypes::register_conversion::<f32, Option<Decimal>>(|v| Decimal::try_from_f32(*v).map(Some));
450        ValueTypes::register_conversion::<Decimal, f64>(|v| Some(v.to_f64()));
451        ValueTypes::register_conversion::<Decimal, Option<f64>>(|v| Some(Some(v.to_f64())));
452        ValueTypes::register_conversion::<Decimal, f32>(|v| Some(v.to_f32()));
453        ValueTypes::register_conversion::<Decimal, Option<f32>>(|v| Some(Some(v.to_f32())));
454
455        // A decimal becomes an integer rounded, midpoints to even, when the
456        // integer is in the range of the type (`Convert.ToInt32(decimal)`
457        // and its siblings).
458        macro_rules! to_integers {
459            ($($ty:ty),*) => {
460                $(
461                    ValueTypes::register_conversion::<Decimal, $ty>(|v| <$ty>::try_from(v.round_to_integer()).ok());
462                    ValueTypes::register_conversion::<Decimal, Option<$ty>>(|v| {
463                        <$ty>::try_from(v.round_to_integer()).ok().map(Some)
464                    });
465                )*
466            };
467        }
468        to_integers!(i8, i16, i32, i64, u8, u16, u32, u64, isize, usize);
469    }
470}
471
472crate::ferro_markup_type!(struct Decimal {
473    namespace: "System",
474    handles: [Decimal],
475    parse: Decimal::parse,
476    constructors: [() => Decimal::default],
477});
478
479impl Default for Decimal {
480    #[inline]
481    fn default() -> Self {
482        Decimal::ZERO
483    }
484}
485
486impl fmt::Display for Decimal {
487    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
488        // The general format of the invariant culture: every digit of the
489        // value, trailing zeros included; negative zero without its sign.
490        f.pad(&self.to_string_provider(&NumberFormatInfo::invariant_info()))
491    }
492}
493
494impl fmt::Debug for Decimal {
495    #[inline]
496    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
497        fmt::Display::fmt(self, f)
498    }
499}
500
501impl FromStr for Decimal {
502    type Err = NumberParseError;
503
504    #[inline]
505    fn from_str(s: &str) -> Result<Self, Self::Err> {
506        Decimal::parse(s)
507    }
508}
509
510macro_rules! from_integers {
511    ($($ty:ty),*) => {
512        $(
513            impl From<$ty> for Decimal {
514                #[inline]
515                fn from(value: $ty) -> Decimal {
516                    Decimal(rust_decimal::Decimal::from(value))
517                }
518            }
519        )*
520    };
521}
522
523from_integers!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
524
525impl Neg for Decimal {
526    type Output = Decimal;
527
528    #[inline]
529    fn neg(self) -> Decimal {
530        Decimal(-self.0)
531    }
532}
533
534macro_rules! operators {
535    ($($trait_:ident $method:ident $checked:ident;)*) => {
536        $(
537            impl $trait_ for Decimal {
538                type Output = Decimal;
539
540                #[inline]
541                fn $method(self, other: Decimal) -> Decimal {
542                    self.$checked(other).expect(OVERFLOW)
543                }
544            }
545        )*
546    };
547}
548
549operators! {
550    Add add checked_add;
551    Sub sub checked_sub;
552    Div div checked_div;
553}
554
555impl AddAssign for Decimal {
556    #[inline]
557    fn add_assign(&mut self, other: Decimal) {
558        *self = *self + other;
559    }
560}
561
562impl SubAssign for Decimal {
563    #[inline]
564    fn sub_assign(&mut self, other: Decimal) {
565        *self = *self - other;
566    }
567}
568
569#[cfg(test)]
570mod tests {
571    mod double_conversion_data {
572        include!("decimal_double_conversion_data.rs");
573    }
574
575    use super::*;
576    use std::collections::hash_map::DefaultHasher;
577    use std::hash::{Hash, Hasher};
578
579    fn d(text: &str) -> Decimal {
580        Decimal::parse(text).unwrap()
581    }
582
583    fn hash_of(value: Decimal) -> u64 {
584        let mut hasher = DefaultHasher::new();
585        value.hash(&mut hasher);
586        hasher.finish()
587    }
588
589    #[test]
590    fn double_conversions_fail_outside_the_value_range() {
591        assert!(std::panic::catch_unwind(|| Decimal::from_f64(f64::NAN)).is_err());
592        assert!(std::panic::catch_unwind(|| Decimal::from_f64(1e30)).is_err());
593        assert!(std::panic::catch_unwind(|| Decimal::from_f64(f64::INFINITY)).is_err());
594        assert!(!Decimal::from_f64(-0.0).is_sign_negative());
595        assert!(!Decimal::from_f64(-1e-30).is_sign_negative());
596    }
597
598    #[test]
599    fn conversion_from_double_matches_the_managed_runtime() {
600        for &(bits, expected) in double_conversion_data::FROM_DOUBLE {
601            let value = f64::from_bits(bits);
602            if expected == "!" {
603                assert!(std::panic::catch_unwind(|| Decimal::from_f64(value)).is_err(), "{value:e}");
604            } else {
605                assert_eq!(Decimal::from_f64(value).to_string(), expected, "{value:e} ({bits:#018X})");
606            }
607        }
608    }
609
610    #[test]
611    fn conversion_to_double_matches_the_managed_runtime() {
612        for &(text, bits) in double_conversion_data::TO_DOUBLE {
613            let actual = d(text).to_f64();
614            assert_eq!(actual.to_bits(), bits, "{text}: {actual:e} instead of {:e}", f64::from_bits(bits));
615        }
616    }
617
618    #[test]
619    fn constants_and_parts() {
620        assert_eq!(Decimal::MAX_VALUE.to_string(), "79228162514264337593543950335");
621        assert_eq!(Decimal::MIN_VALUE.to_string(), "-79228162514264337593543950335");
622        assert_eq!(Decimal::ZERO.to_string(), "0");
623        assert_eq!(Decimal::ONE.to_string(), "1");
624        assert_eq!(Decimal::from_parts(12345, 3, true).to_string(), "-12.345");
625        assert_eq!(Decimal::from_parts(5, 3, false).to_string(), "0.005");
626        assert_eq!(Decimal::from_parts(110, 2, false).mantissa(), 110);
627        assert_eq!(Decimal::from_parts(110, 2, false).scale(), 2);
628        assert_eq!(Decimal::MAX_VALUE.mantissa(), MAX_MANTISSA);
629        assert!(Decimal::from_parts(0, 1, true).is_sign_negative());
630        assert!(Decimal::from_parts(0, 1, true).is_zero());
631        assert_eq!(Decimal::default(), Decimal::ZERO);
632    }
633
634    #[test]
635    fn display_preserves_trailing_zeros() {
636        assert_eq!(d("1.10").to_string(), "1.10");
637        assert_eq!(d("1.1").to_string(), "1.1");
638        assert_eq!(d("0.00").to_string(), "0.00");
639        assert_eq!(d("-0.0").to_string(), "0.0");
640        assert_eq!(d("100").to_string(), "100");
641        assert_eq!(d("0.0000000000000000000000000001").to_string(), "0.0000000000000000000000000001");
642        assert_eq!(format!("{:>6}", d("1.5")), "   1.5");
643        assert_eq!(format!("{:?}", d("1.50")), "1.50");
644    }
645
646    #[test]
647    fn equality_ordering_and_hash_are_by_value() {
648        assert_eq!(d("1.10"), d("1.1"));
649        assert_eq!(hash_of(d("1.10")), hash_of(d("1.1")));
650        assert_eq!(d("0.00"), Decimal::ZERO);
651        assert_eq!(d("-0"), Decimal::ZERO);
652        assert_eq!(hash_of(d("-0.00")), hash_of(Decimal::ZERO));
653        assert!(d("1.1") < d("1.11"));
654        assert!(d("-1.1") > d("-1.11"));
655        assert!(d("-1") < d("0.5"));
656        assert!(Decimal::MIN_VALUE < Decimal::MAX_VALUE);
657        assert!(Decimal::MAX_VALUE > d("0.0000000000000000000000000001"));
658        assert_eq!(d("2").max(d("3.0")).to_string(), "3.0");
659        assert_eq!(d("5").min(d("3.0")).to_string(), "3.0");
660        assert_eq!(d("5").clamp(d("200"), d("400")), d("200"));
661    }
662
663    #[test]
664    fn addition_and_subtraction_take_the_larger_scale() {
665        assert_eq!((d("1.10") + d("2.2")).to_string(), "3.30");
666        assert_eq!((d("1.1") - d("1.10")).to_string(), "0.00");
667        assert_eq!((d("1") - d("3")).to_string(), "-2");
668        assert_eq!((d("-1.5") + d("1.5")).to_string(), "0.0");
669        assert_eq!((d("0.1") + d("0.2")), d("0.3"));
670        assert_eq!((d("5") + Decimal::ONE).to_string(), "6");
671        assert_eq!((d("10.11") + Decimal::ONE).to_string(), "11.11");
672        assert_eq!((Decimal::MAX_VALUE - Decimal::ONE).to_string(), "79228162514264337593543950334");
673        let mut value = d("1.5");
674        value += d("1");
675        value -= d("0.25");
676        assert_eq!(value.to_string(), "2.25");
677        assert_eq!((-d("1.50")).to_string(), "-1.50");
678    }
679
680    #[test]
681    fn a_sum_with_a_zero_of_a_larger_scale_takes_the_largest_scale_that_fits() {
682        // Results of the managed runtime.
683        assert_eq!(
684            (d("0.000000000000000000000000000") + d("857337436073678641268323889")).to_string(),
685            "857337436073678641268323889.0"
686        );
687        assert_eq!(
688            (d("0.000000000000000000000000000") - d("857337436073678641268323889")).to_string(),
689            "-857337436073678641268323889.0"
690        );
691        assert_eq!(
692            (d("434.559") + d("0.0000000000000000000000000000")).to_string(),
693            "434.55900000000000000000000000"
694        );
695        assert_eq!(
696            (d("-4128645094226724.54") - d("-0.00000000000000000000")).to_string(),
697            "-4128645094226724.5400000000000"
698        );
699        assert_eq!((Decimal::MAX_VALUE + d("0.00")).to_string(), "79228162514264337593543950335");
700        assert_eq!((d("5") + d("0.00")).to_string(), "5.00");
701    }
702
703    #[test]
704    fn max_and_min_of_equal_values_are_those_of_the_managed_runtime() {
705        // Math.Max returns the first of two equal values, Math.Min the second.
706        assert_eq!(d("5.00").max(d("5")).to_string(), "5.00");
707        assert_eq!(d("5").max(d("5.00")).to_string(), "5");
708        assert_eq!(d("5.00").min(d("5")).to_string(), "5");
709        assert_eq!(d("5").min(d("5.00")).to_string(), "5.00");
710        assert_eq!(d("-0.0").max(d("0")).to_string(), "0.0");
711        assert_eq!(d("2").max(d("3.0")).to_string(), "3.0");
712        assert_eq!(d("5").min(d("3.0")).to_string(), "3.0");
713    }
714
715    #[test]
716    #[should_panic(expected = "Value was either too large or too small for a Decimal.")]
717    fn addition_overflow_panics() {
718        let _ = Decimal::MAX_VALUE + Decimal::ONE;
719    }
720
721    #[test]
722    fn checked_operations_report_overflow() {
723        assert_eq!(Decimal::MAX_VALUE.checked_add(Decimal::ONE), None);
724        assert_eq!(Decimal::MIN_VALUE.checked_sub(Decimal::ONE), None);
725        assert_eq!(Decimal::MAX_VALUE.checked_div(d("0.1")), None);
726        assert_eq!(Decimal::MAX_VALUE.checked_add(Decimal::MIN_VALUE), Some(Decimal::ZERO));
727    }
728
729    #[test]
730    fn division() {
731        // The percent text of the control is divided by one hundred.
732        assert_eq!((d("10.11") / Decimal::from(100)).to_string(), "0.1011");
733        assert_eq!((d("1011.00") / Decimal::from(100)).to_string(), "10.11");
734        assert_eq!((d("50") / Decimal::from(100)).to_string(), "0.5");
735        assert_eq!((d("-1") / d("4")).to_string(), "-0.25");
736        assert_eq!((d("1") / d("3")).to_string(), "0.3333333333333333333333333333");
737        assert_eq!((d("6.00") / d("2")).to_string(), "3.00");
738        assert_eq!((d("6") / d("2.00")).to_string(), "3");
739        assert_eq!((d("1.00") / d("8")).to_string(), "0.125");
740        assert_eq!((d("0") / d("7")).to_string(), "0");
741    }
742
743    #[test]
744    #[should_panic(expected = "Attempted to divide by zero.")]
745    fn division_by_zero_panics() {
746        let _ = Decimal::ONE / Decimal::ZERO;
747    }
748
749    #[test]
750    fn integer_conversions() {
751        assert_eq!(Decimal::from(42i32).to_string(), "42");
752        assert_eq!(Decimal::from(-42i64).to_string(), "-42");
753        assert_eq!(Decimal::from(i64::MIN).to_string(), "-9223372036854775808");
754        assert_eq!(Decimal::from(u64::MAX).to_string(), "18446744073709551615");
755    }
756
757    #[test]
758    fn single_conversions() {
759        // Seven significant digits.
760        assert_eq!(Decimal::from_f32(0.1).to_string(), "0.1");
761        assert_eq!(Decimal::from_f32(-2.5).to_string(), "-2.5");
762        assert_eq!(Decimal::from_f32(1234.5).to_string(), "1234.5");
763        assert_eq!(Decimal::from_f32(16777216.0).to_string(), "16777220");
764        assert_eq!(Decimal::from_f32(1e10).to_string(), "10000000000");
765        assert_eq!(Decimal::from_f32(0.0).to_string(), "0");
766        assert!(!Decimal::from_f32(-1e-30).is_sign_negative());
767        assert_eq!(Decimal::try_from_f32(f32::NAN), None);
768        assert_eq!(Decimal::try_from_f32(f32::INFINITY), None);
769        assert_eq!(Decimal::try_from_f32(1e30), None);
770        assert!(std::panic::catch_unwind(|| Decimal::from_f32(f32::NEG_INFINITY)).is_err());
771        assert_eq!(Decimal::try_from_f64(f64::NAN), None);
772        assert_eq!(Decimal::try_from_f64(-1e30), None);
773        assert_eq!(Decimal::try_from_f64(2.5), Some(d("2.5")));
774
775        assert_eq!(d("0.5").to_f32(), 0.5);
776        assert_eq!(d("0.1").to_f32(), 0.1f32);
777        assert_eq!(Decimal::MAX_VALUE.to_f32(), 7.9228163e28);
778    }
779
780    #[test]
781    fn rounding_to_an_integer_takes_midpoints_to_even() {
782        assert_eq!(d("0").round_to_integer(), 0);
783        assert_eq!(d("7").round_to_integer(), 7);
784        assert_eq!(d("0.5").round_to_integer(), 0);
785        assert_eq!(d("1.5").round_to_integer(), 2);
786        assert_eq!(d("2.5").round_to_integer(), 2);
787        assert_eq!(d("2.50").round_to_integer(), 2);
788        assert_eq!(d("2.51").round_to_integer(), 3);
789        assert_eq!(d("2.49").round_to_integer(), 2);
790        assert_eq!(d("-1.5").round_to_integer(), -2);
791        assert_eq!(d("-2.5").round_to_integer(), -2);
792        assert_eq!(d("-0.4").round_to_integer(), 0);
793        assert_eq!(Decimal::MAX_VALUE.round_to_integer(), MAX_MANTISSA as i128);
794        assert_eq!(Decimal::MIN_VALUE.round_to_integer(), -(MAX_MANTISSA as i128));
795    }
796
797    #[test]
798    fn parse_and_from_str() {
799        assert_eq!("1.10".parse::<Decimal>().unwrap().to_string(), "1.10");
800        assert_eq!(d(" -1,234.50 ").to_string(), "-1234.50");
801        assert_eq!(d("5-").to_string(), "-5");
802        assert_eq!(d("+5").to_string(), "5");
803        assert_eq!(d(".5").to_string(), "0.5");
804        assert_eq!(d("5.").to_string(), "5");
805        assert_eq!(Decimal::parse(""), Err(NumberParseError::Format));
806        assert_eq!(Decimal::parse("abc"), Err(NumberParseError::Format));
807        assert_eq!(Decimal::parse("1e2"), Err(NumberParseError::Format));
808        assert_eq!(Decimal::parse("$5"), Err(NumberParseError::Format));
809        assert_eq!(Decimal::parse("79228162514264337593543950336"), Err(NumberParseError::Overflow));
810        assert_eq!(Decimal::try_parse("x"), None);
811        assert_eq!(d("79228162514264337593543950335"), Decimal::MAX_VALUE);
812        assert_eq!(d("-79228162514264337593543950335"), Decimal::MIN_VALUE);
813        // More digits than the precision holds are rounded half to even.
814        assert_eq!(d("0.00000000000000000000000000005").to_string(), "0.0000000000000000000000000000");
815        assert_eq!(d("0.00000000000000000000000000015").to_string(), "0.0000000000000000000000000002");
816        assert_eq!(d("0.000000000000000000000000000051").to_string(), "0.0000000000000000000000000001");
817        assert_eq!(d("1.00000000000000000000000000005").to_string(), "1.0000000000000000000000000000");
818        assert_eq!(d("7922816251426433759354395033.55").to_string(), "7922816251426433759354395034");
819    }
820
821    #[test]
822    fn untyped_conversions() {
823        use crate::data::core::ValueType;
824        use crate::BoxedValue;
825        use std::rc::Rc;
826
827        let convert = |value: BoxedValue, target: ValueType| ValueTypes::try_convert_registered(&value, target);
828        let text: BoxedValue = Rc::new("1.50".to_string());
829        let converted = convert(text, ValueType::of::<Decimal>()).unwrap();
830        assert_eq!(converted.downcast_ref::<Decimal>().unwrap().to_string(), "1.50");
831
832        let number: BoxedValue = Rc::new(7i32);
833        let converted = convert(number, ValueType::of::<Option<Decimal>>()).unwrap();
834        assert_eq!(converted.downcast_ref::<Option<Decimal>>(), Some(&Some(d("7"))));
835
836        let value: BoxedValue = Rc::new(d("2.5"));
837        assert_eq!(
838            convert(value.clone(), ValueType::of::<Option<Decimal>>()).unwrap().downcast_ref::<Option<Decimal>>(),
839            Some(&Some(d("2.5")))
840        );
841        assert_eq!(ValueTypes::to_display_string(Some(&value)), "2.5");
842
843        assert!(ValueTypes::accepts_null(ValueType::of::<Option<Decimal>>()));
844        assert!(!ValueTypes::accepts_null(ValueType::of::<Decimal>()));
845    }
846}