1use crate::{
2 constants::{BYTES_TO_OVERFLOW_U64, MAX_SCALE, MAX_STR_BUFFER_SIZE, OVERFLOW_U96, WILL_OVERFLOW_U64},
3 error::{tail_error, Error},
4 ops::array::{add_by_internal_flattened, add_one_internal, div_by_u32, is_all_zero, mul_by_u32},
5 Decimal,
6};
7
8use arrayvec::{ArrayString, ArrayVec};
9
10use alloc::{string::String, vec::Vec};
11use core::fmt;
12
13pub(crate) fn to_str_internal(
15 value: &Decimal,
16 append_sign: bool,
17 precision: Option<usize>,
18) -> (ArrayString<MAX_STR_BUFFER_SIZE>, Option<usize>) {
19 let scale = value.scale() as usize;
21
22 let mut chars = ArrayVec::<_, MAX_STR_BUFFER_SIZE>::new();
24 let mut working = value.mantissa_array3();
25 while !is_all_zero(&working) {
26 let remainder = div_by_u32(&mut working, 10u32);
27 chars.push(char::from(b'0' + remainder as u8));
28 }
29 while scale > chars.len() {
30 chars.push('0');
31 }
32
33 let (prec, additional) = match precision {
34 Some(prec) => {
35 let max: usize = MAX_SCALE.into();
36 if prec > max {
37 (max, Some(prec - max))
38 } else {
39 (prec, None)
40 }
41 }
42 None => (scale, None),
43 };
44
45 let len = chars.len();
46 let whole_len = len - scale;
47 let mut rep = ArrayString::new();
48 let empty_len = if append_sign && value.is_sign_negative() {
50 rep.push('-');
51 1
52 } else {
53 0
54 };
55 for i in 0..whole_len + prec {
56 if i == len - scale {
57 if i == 0 {
58 rep.push('0');
59 }
60 rep.push('.');
61 }
62
63 if i >= len {
64 rep.push('0');
65 } else {
66 let c = chars[len - i - 1];
67 rep.push(c);
68 }
69 }
70
71 if rep.len() == empty_len {
73 rep.push('0');
74 }
75
76 (rep, additional)
77}
78
79pub(crate) fn fmt_scientific_notation(
80 value: &Decimal,
81 exponent_symbol: &str,
82 f: &mut fmt::Formatter<'_>,
83) -> fmt::Result {
84 #[cfg(not(feature = "std"))]
85 use alloc::string::ToString;
86
87 if value.is_zero() {
88 return f.write_str("0e0");
89 }
90
91 let mut exponent = -(value.scale() as isize);
93
94 let mut chars = Vec::new();
96 let mut working = value.mantissa_array3();
97 while !is_all_zero(&working) {
98 let remainder = div_by_u32(&mut working, 10u32);
99 chars.push(char::from(b'0' + remainder as u8));
100 }
101
102 let len = chars.len();
109 let mut rep;
110 if let Some(precision) = f.precision() {
113 if len > 1 {
114 if precision == 0 && chars.iter().take(len - 1).all(|c| *c == '0') {
116 rep = chars.iter().skip(len - 1).collect::<String>();
117 } else {
118 if precision > 0 {
120 chars.insert(len - 1, '.');
121 }
122 rep = chars
123 .iter()
124 .rev()
125 .chain(core::iter::repeat(&'0'))
127 .take(if precision == 0 { 1 } else { 2 + precision })
128 .collect::<String>();
129 }
130 exponent += (len - 1) as isize;
131 } else if precision > 0 {
132 chars.push('.');
134 rep = chars
135 .iter()
136 .chain(core::iter::repeat(&'0'))
137 .take(2 + precision)
138 .collect::<String>();
139 } else {
140 rep = chars.iter().collect::<String>();
141 }
142 } else if len > 1 {
143 if chars.iter().take(len - 1).all(|c| *c == '0') {
145 rep = chars.iter().skip(len - 1).collect::<String>();
146 } else {
147 chars.insert(len - 1, '.');
149 rep = chars.iter().rev().collect::<String>();
150 }
151 exponent += (len - 1) as isize;
152 } else {
153 rep = chars.iter().collect::<String>();
154 }
155
156 rep.push_str(exponent_symbol);
157 rep.push_str(&exponent.to_string());
158 f.pad_integral(value.is_sign_positive(), "", &rep)
159}
160
161#[inline]
163pub(crate) fn parse_str_radix_10(str: &str) -> Result<Decimal, Error> {
164 let bytes = str.as_bytes();
165 if bytes.len() < BYTES_TO_OVERFLOW_U64 {
166 parse_str_radix_10_dispatch::<false, true>(bytes)
167 } else {
168 parse_str_radix_10_dispatch::<true, true>(bytes)
169 }
170}
171
172#[inline]
173pub(crate) fn parse_str_radix_10_exact(str: &str) -> Result<Decimal, Error> {
174 let bytes = str.as_bytes();
175 if bytes.len() < BYTES_TO_OVERFLOW_U64 {
176 parse_str_radix_10_dispatch::<false, false>(bytes)
177 } else {
178 parse_str_radix_10_dispatch::<true, false>(bytes)
179 }
180}
181
182#[inline]
183fn parse_str_radix_10_dispatch<const BIG: bool, const ROUND: bool>(bytes: &[u8]) -> Result<Decimal, Error> {
184 match bytes {
185 [b, rest @ ..] => byte_dispatch_u64::<false, false, false, BIG, true, ROUND>(rest, 0, 0, *b),
186 [] => tail_error("Invalid decimal: empty"),
187 }
188}
189
190#[inline]
191fn overflow_64(val: u64) -> bool {
192 val >= WILL_OVERFLOW_U64
193}
194
195#[inline]
196pub fn overflow_128(val: u128) -> bool {
197 val >= OVERFLOW_U96
198}
199
200#[inline]
208fn dispatch_next<const POINT: bool, const NEG: bool, const HAS: bool, const BIG: bool, const ROUND: bool>(
209 bytes: &[u8],
210 data64: u64,
211 scale: u8,
212) -> Result<Decimal, Error> {
213 if let Some((next, bytes)) = bytes.split_first() {
214 byte_dispatch_u64::<POINT, NEG, HAS, BIG, false, ROUND>(bytes, data64, scale, *next)
215 } else {
216 handle_data::<NEG, HAS>(data64 as u128, scale)
217 }
218}
219
220#[inline(never)]
229fn non_digit_dispatch_u64<
230 const POINT: bool,
231 const NEG: bool,
232 const HAS: bool,
233 const BIG: bool,
234 const FIRST: bool,
235 const ROUND: bool,
236>(
237 bytes: &[u8],
238 data64: u64,
239 scale: u8,
240 b: u8,
241) -> Result<Decimal, Error> {
242 match b {
243 b'-' if FIRST && !HAS => dispatch_next::<false, true, false, BIG, ROUND>(bytes, data64, scale),
244 b'+' if FIRST && !HAS => dispatch_next::<false, false, false, BIG, ROUND>(bytes, data64, scale),
245 b'_' if HAS => handle_separator::<POINT, NEG, BIG, ROUND>(bytes, data64, scale),
246 b => tail_invalid_digit(b),
247 }
248}
249
250#[inline]
251fn byte_dispatch_u64<
252 const POINT: bool,
253 const NEG: bool,
254 const HAS: bool,
255 const BIG: bool,
256 const FIRST: bool,
257 const ROUND: bool,
258>(
259 bytes: &[u8],
260 data64: u64,
261 scale: u8,
262 b: u8,
263) -> Result<Decimal, Error> {
264 match b {
265 b'0'..=b'9' => handle_digit_64::<POINT, NEG, BIG, ROUND>(bytes, data64, scale, b - b'0'),
266 b'.' if !POINT => handle_point::<NEG, HAS, BIG, ROUND>(bytes, data64, scale),
267 b => non_digit_dispatch_u64::<POINT, NEG, HAS, BIG, FIRST, ROUND>(bytes, data64, scale, b),
268 }
269}
270
271#[inline(never)]
272fn handle_digit_64<const POINT: bool, const NEG: bool, const BIG: bool, const ROUND: bool>(
273 bytes: &[u8],
274 data64: u64,
275 scale: u8,
276 digit: u8,
277) -> Result<Decimal, Error> {
278 let data64 = data64 * 10 + digit as u64;
280 let scale = if POINT { scale + 1 } else { 0 };
281
282 if let Some((next, bytes)) = bytes.split_first() {
283 let next = *next;
284 if POINT && BIG && scale >= 28 {
285 if ROUND {
286 maybe_round(data64 as u128, next, scale, POINT, NEG)
287 } else {
288 Err(Error::Underflow)
289 }
290 } else if BIG && overflow_64(data64) {
291 handle_full_128::<POINT, NEG, ROUND>(data64 as u128, bytes, scale, next)
292 } else {
293 byte_dispatch_u64::<POINT, NEG, true, BIG, false, ROUND>(bytes, data64, scale, next)
294 }
295 } else {
296 let data: u128 = data64 as u128;
297
298 handle_data::<NEG, true>(data, scale)
299 }
300}
301
302#[inline(never)]
303fn handle_point<const NEG: bool, const HAS: bool, const BIG: bool, const ROUND: bool>(
304 bytes: &[u8],
305 data64: u64,
306 scale: u8,
307) -> Result<Decimal, Error> {
308 dispatch_next::<true, NEG, HAS, BIG, ROUND>(bytes, data64, scale)
309}
310
311#[inline(never)]
312fn handle_separator<const POINT: bool, const NEG: bool, const BIG: bool, const ROUND: bool>(
313 bytes: &[u8],
314 data64: u64,
315 scale: u8,
316) -> Result<Decimal, Error> {
317 dispatch_next::<POINT, NEG, true, BIG, ROUND>(bytes, data64, scale)
318}
319
320#[inline(never)]
321#[cold]
322fn tail_invalid_digit(digit: u8) -> Result<Decimal, Error> {
323 match digit {
324 b'.' => tail_error("Invalid decimal: two decimal points"),
325 b'_' => tail_error("Invalid decimal: must start lead with a number"),
326 _ => tail_error("Invalid decimal: unknown character"),
327 }
328}
329
330#[inline(never)]
331#[cold]
332fn handle_full_128<const POINT: bool, const NEG: bool, const ROUND: bool>(
333 mut data: u128,
334 bytes: &[u8],
335 scale: u8,
336 next_byte: u8,
337) -> Result<Decimal, Error> {
338 let b = next_byte;
339 match b {
340 b'0'..=b'9' => {
341 let digit = u32::from(b - b'0');
342
343 let next = (data * 10) + digit as u128;
345 if overflow_128(next) {
346 if !POINT {
347 return tail_error("Invalid decimal: overflow from too many digits");
348 }
349
350 if ROUND {
351 maybe_round(data, next_byte, scale, POINT, NEG)
352 } else {
353 Err(Error::Underflow)
354 }
355 } else {
356 data = next;
357 let scale = scale + POINT as u8;
358 if let Some((next, bytes)) = bytes.split_first() {
359 let next = *next;
360 if POINT && scale >= 28 {
361 if ROUND {
362 if next == b'_' {
364 let mut remaining_bytes = bytes;
366 let mut next_char = None;
367 while let Some((n, rest)) = remaining_bytes.split_first() {
368 if *n != b'_' {
369 next_char = Some(*n);
370 break;
371 }
372 remaining_bytes = rest;
373 }
374
375 if let Some(ch) = next_char {
376 maybe_round(data, ch, scale, POINT, NEG)
378 } else {
379 handle_data::<NEG, true>(data, scale)
380 }
381 } else {
382 maybe_round(data, next, scale, POINT, NEG)
384 }
385 } else {
386 Err(Error::Underflow)
387 }
388 } else {
389 handle_full_128::<POINT, NEG, ROUND>(data, bytes, scale, next)
390 }
391 } else {
392 handle_data::<NEG, true>(data, scale)
393 }
394 }
395 }
396 b'.' if !POINT => {
397 if let Some((next, bytes)) = bytes.split_first() {
399 handle_full_128::<true, NEG, ROUND>(data, bytes, scale, *next)
400 } else {
401 handle_data::<NEG, true>(data, scale)
402 }
403 }
404 b'_' => {
405 if let Some((next, bytes)) = bytes.split_first() {
406 handle_full_128::<POINT, NEG, ROUND>(data, bytes, scale, *next)
407 } else {
408 handle_data::<NEG, true>(data, scale)
409 }
410 }
411 b => tail_invalid_digit(b),
412 }
413}
414
415#[inline(never)]
416#[cold]
417fn maybe_round(mut data: u128, next_byte: u8, mut scale: u8, point: bool, negative: bool) -> Result<Decimal, Error> {
418 let digit = match next_byte {
419 b'0'..=b'9' => u32::from(next_byte - b'0'),
420 b'_' => 0, b'.' if !point => 0,
422 b => return tail_invalid_digit(b),
423 };
424
425 if digit >= 5 {
427 data += 1;
428
429 if overflow_128(data) {
432 if scale == 0 {
433 return tail_error("Invalid decimal: overflow from mantissa after rounding");
434 }
435 data += 4;
436 data /= 10;
437 scale -= 1;
438 }
439 }
440
441 if negative {
442 handle_data::<true, true>(data, scale)
443 } else {
444 handle_data::<false, true>(data, scale)
445 }
446}
447
448#[inline(never)]
449fn tail_no_has() -> Result<Decimal, Error> {
450 tail_error("Invalid decimal: no digits found")
451}
452
453#[inline]
454fn handle_data<const NEG: bool, const HAS: bool>(data: u128, scale: u8) -> Result<Decimal, Error> {
455 debug_assert_eq!(data >> 96, 0);
456 if !HAS {
457 tail_no_has()
458 } else {
459 Ok(Decimal::from_parts(
460 data as u32,
461 (data >> 32) as u32,
462 (data >> 64) as u32,
463 NEG,
464 scale as u32,
465 ))
466 }
467}
468
469pub(crate) fn parse_str_radix_n(str: &str, radix: u32) -> Result<Decimal, Error> {
470 if str.is_empty() {
471 return Err(Error::from("Invalid decimal: empty"));
472 }
473 if radix < 2 {
474 return Err(Error::from("Unsupported radix < 2"));
475 }
476 if radix > 36 {
477 return Err(Error::from("Unsupported radix > 36"));
479 }
480
481 let mut offset = 0;
482 let mut len = str.len();
483 let bytes = str.as_bytes();
484 let mut negative = false; if bytes[offset] == b'-' {
488 negative = true; offset += 1;
490 len -= 1;
491 } else if bytes[offset] == b'+' {
492 offset += 1;
494 len -= 1;
495 }
496
497 let mut digits_before_dot: i32 = -1; let mut coeff = ArrayVec::<_, 96>::new(); let (max_n, max_alpha_lower, max_alpha_upper) = if radix <= 10 {
503 (b'0' + (radix - 1) as u8, 0, 0)
504 } else {
505 let adj = (radix - 11) as u8;
506 (b'9', adj + b'a', adj + b'A')
507 };
508
509 let estimated_max_precision = match radix {
513 2 => 96,
514 3 => 61,
515 4 => 48,
516 5 => 42,
517 6 => 38,
518 7 => 35,
519 8 => 32,
520 9 => 31,
521 10 => 28,
522 11 => 28,
523 12 => 27,
524 13 => 26,
525 14 => 26,
526 15 => 25,
527 16 => 24,
528 17 => 24,
529 18 => 24,
530 19 => 23,
531 20 => 23,
532 21 => 22,
533 22 => 22,
534 23 => 22,
535 24 => 21,
536 25 => 21,
537 26 => 21,
538 27 => 21,
539 28 => 20,
540 29 => 20,
541 30 => 20,
542 31 => 20,
543 32 => 20,
544 33 => 20,
545 34 => 19,
546 35 => 19,
547 36 => 19,
548 _ => return Err(Error::from("Unsupported radix")),
549 };
550
551 let mut maybe_round = false;
552 while len > 0 {
553 let b = bytes[offset];
554 match b {
555 b'0'..=b'9' => {
556 if b > max_n {
557 return Err(Error::from("Invalid decimal: invalid character"));
558 }
559 coeff.push(u32::from(b - b'0'));
560 offset += 1;
561 len -= 1;
562
563 if coeff.len() as u32 > estimated_max_precision {
565 maybe_round = true;
566 break;
567 }
568 }
569 b'a'..=b'z' => {
570 if b > max_alpha_lower {
571 return Err(Error::from("Invalid decimal: invalid character"));
572 }
573 coeff.push(u32::from(b - b'a') + 10);
574 offset += 1;
575 len -= 1;
576
577 if coeff.len() as u32 > estimated_max_precision {
578 maybe_round = true;
579 break;
580 }
581 }
582 b'A'..=b'Z' => {
583 if b > max_alpha_upper {
584 return Err(Error::from("Invalid decimal: invalid character"));
585 }
586 coeff.push(u32::from(b - b'A') + 10);
587 offset += 1;
588 len -= 1;
589
590 if coeff.len() as u32 > estimated_max_precision {
591 maybe_round = true;
592 break;
593 }
594 }
595 b'.' => {
596 if digits_before_dot >= 0 {
597 return Err(Error::from("Invalid decimal: two decimal points"));
598 }
599 digits_before_dot = coeff.len() as i32;
600 offset += 1;
601 len -= 1;
602 }
603 b'_' => {
604 if coeff.is_empty() {
606 return Err(Error::from("Invalid decimal: must start lead with a number"));
607 }
608 offset += 1;
609 len -= 1;
610 }
611 _ => return Err(Error::from("Invalid decimal: unknown character")),
612 }
613 }
614
615 if maybe_round && offset < bytes.len() {
617 let next_byte = bytes[offset];
618 let digit = match next_byte {
619 b'0'..=b'9' => {
620 if next_byte > max_n {
621 return Err(Error::from("Invalid decimal: invalid character"));
622 }
623 u32::from(next_byte - b'0')
624 }
625 b'a'..=b'z' => {
626 if next_byte > max_alpha_lower {
627 return Err(Error::from("Invalid decimal: invalid character"));
628 }
629 u32::from(next_byte - b'a') + 10
630 }
631 b'A'..=b'Z' => {
632 if next_byte > max_alpha_upper {
633 return Err(Error::from("Invalid decimal: invalid character"));
634 }
635 u32::from(next_byte - b'A') + 10
636 }
637 b'_' => 0,
638 b'.' => {
639 if digits_before_dot >= 0 {
641 return Err(Error::from("Invalid decimal: two decimal points"));
642 }
643 0
644 }
645 _ => return Err(Error::from("Invalid decimal: unknown character")),
646 };
647
648 let midpoint = if radix & 0x1 == 1 { radix / 2 } else { (radix + 1) / 2 };
650 if digit >= midpoint {
651 let mut index = coeff.len() - 1;
652 loop {
653 let new_digit = coeff[index] + 1;
654 if new_digit <= 9 {
655 coeff[index] = new_digit;
656 break;
657 } else {
658 coeff[index] = 0;
659 if index == 0 {
660 coeff.insert(0, 1u32);
661 digits_before_dot += 1;
662 coeff.pop();
663 break;
664 }
665 }
666 index -= 1;
667 }
668 }
669 }
670
671 if coeff.is_empty() {
673 return Err(Error::from("Invalid decimal: no digits found"));
674 }
675
676 let mut scale = if digits_before_dot >= 0 {
677 (coeff.len() as u32) - (digits_before_dot as u32)
679 } else {
680 0
681 };
682
683 let mut data = [0u32, 0u32, 0u32];
685 let mut tmp = [0u32, 0u32, 0u32];
686 let len = coeff.len();
687 for (i, digit) in coeff.iter().enumerate() {
688 tmp[0] = data[0];
690 tmp[1] = data[1];
691 tmp[2] = data[2];
692 let overflow = mul_by_u32(&mut tmp, radix);
693 if overflow > 0 {
694 if (i as i32) < digits_before_dot && i + 1 < len {
698 return Err(Error::from("Invalid decimal: overflow from too many digits"));
699 }
700
701 if *digit >= 5 {
702 let carry = add_one_internal(&mut data);
703 if carry > 0 {
704 return Err(Error::from("Invalid decimal: overflow when rounding"));
706 }
707 }
708 let diff = (len - i) as u32;
710 if diff > scale {
711 return Err(Error::from("Invalid decimal: overflow from scale mismatch"));
712 }
713 scale -= diff;
714 break;
715 } else {
716 data[0] = tmp[0];
717 data[1] = tmp[1];
718 data[2] = tmp[2];
719 let carry = add_by_internal_flattened(&mut data, *digit);
720 if carry > 0 {
721 return Err(Error::from("Invalid decimal: overflow from carry"));
723 }
724 }
725 }
726
727 Ok(Decimal::from_parts(data[0], data[1], data[2], negative, scale))
728}
729
730#[cfg(test)]
731mod test {
732 use super::*;
733 use crate::Decimal;
734 use arrayvec::ArrayString;
735 use core::{fmt::Write, str::FromStr};
736
737 #[test]
738 fn display_does_not_overflow_max_capacity() {
739 let num = Decimal::from_str("1.2").unwrap();
740 let mut buffer = ArrayString::<64>::new();
741 buffer.write_fmt(format_args!("{num:.31}")).unwrap();
742 assert_eq!("1.2000000000000000000000000000000", buffer.as_str());
743 }
744
745 #[test]
746 fn from_str_rounding_0() {
747 assert_eq!(
748 parse_str_radix_10("1.234").unwrap().unpack(),
749 Decimal::new(1234, 3).unpack()
750 );
751 }
752
753 #[test]
754 fn from_str_rounding_1() {
755 assert_eq!(
756 parse_str_radix_10("11111_11111_11111.11111_11111_11111")
757 .unwrap()
758 .unpack(),
759 Decimal::from_i128_with_scale(11_111_111_111_111_111_111_111_111_111, 14).unpack()
760 );
761 }
762
763 #[test]
764 fn from_str_rounding_2() {
765 assert_eq!(
766 parse_str_radix_10("11111_11111_11111.11111_11111_11115")
767 .unwrap()
768 .unpack(),
769 Decimal::from_i128_with_scale(11_111_111_111_111_111_111_111_111_112, 14).unpack()
770 );
771 }
772
773 #[test]
774 fn from_str_rounding_3() {
775 assert_eq!(
776 parse_str_radix_10("11111_11111_11111.11111_11111_11195")
777 .unwrap()
778 .unpack(),
779 Decimal::from_i128_with_scale(1_111_111_111_111_111_111_111_111_1120, 14).unpack() );
781 }
782
783 #[test]
784 fn from_str_rounding_4() {
785 assert_eq!(
786 parse_str_radix_10("99999_99999_99999.99999_99999_99995")
787 .unwrap()
788 .unpack(),
789 Decimal::from_i128_with_scale(10_000_000_000_000_000_000_000_000_000, 13).unpack() );
791 }
792
793 #[test]
794 fn from_str_no_rounding_0() {
795 assert_eq!(
796 parse_str_radix_10_exact("1.234").unwrap().unpack(),
797 Decimal::new(1234, 3).unpack()
798 );
799 }
800
801 #[test]
802 fn from_str_no_rounding_1() {
803 assert_eq!(
804 parse_str_radix_10_exact("11111_11111_11111.11111_11111_11111"),
805 Err(Error::Underflow)
806 );
807 }
808
809 #[test]
810 fn from_str_no_rounding_2() {
811 assert_eq!(
812 parse_str_radix_10_exact("11111_11111_11111.11111_11111_11115"),
813 Err(Error::Underflow)
814 );
815 }
816
817 #[test]
818 fn from_str_no_rounding_3() {
819 assert_eq!(
820 parse_str_radix_10_exact("11111_11111_11111.11111_11111_11195"),
821 Err(Error::Underflow)
822 );
823 }
824
825 #[test]
826 fn from_str_no_rounding_4() {
827 assert_eq!(
828 parse_str_radix_10_exact("99999_99999_99999.99999_99999_99995"),
829 Err(Error::Underflow)
830 );
831 }
832
833 #[test]
834 fn from_str_many_pointless_chars() {
835 assert_eq!(
836 parse_str_radix_10("00________________________________________________________________001.1")
837 .unwrap()
838 .unpack(),
839 Decimal::from_i128_with_scale(11, 1).unpack()
840 );
841 }
842
843 #[test]
844 fn from_str_leading_0s_1() {
845 assert_eq!(
846 parse_str_radix_10("00001.1").unwrap().unpack(),
847 Decimal::from_i128_with_scale(11, 1).unpack()
848 );
849 }
850
851 #[test]
852 fn from_str_leading_0s_2() {
853 assert_eq!(
854 parse_str_radix_10("00000_00000_00000_00000_00001.00001")
855 .unwrap()
856 .unpack(),
857 Decimal::from_i128_with_scale(100001, 5).unpack()
858 );
859 }
860
861 #[test]
862 fn from_str_leading_0s_3() {
863 assert_eq!(
864 parse_str_radix_10("0.00000_00000_00000_00000_00000_00100")
865 .unwrap()
866 .unpack(),
867 Decimal::from_i128_with_scale(1, 28).unpack()
868 );
869 }
870
871 #[test]
872 fn from_str_trailing_0s_1() {
873 assert_eq!(
874 parse_str_radix_10("0.00001_00000_00000").unwrap().unpack(),
875 Decimal::from_i128_with_scale(10_000_000_000, 15).unpack()
876 );
877 }
878
879 #[test]
880 fn from_str_trailing_0s_2() {
881 assert_eq!(
882 parse_str_radix_10("0.00001_00000_00000_00000_00000_00000")
883 .unwrap()
884 .unpack(),
885 Decimal::from_i128_with_scale(100_000_000_000_000_000_000_000, 28).unpack()
886 );
887 }
888
889 #[test]
890 fn from_str_overflow_1() {
891 assert_eq!(
892 parse_str_radix_10("99999_99999_99999_99999_99999_99999.99999"),
893 Err(Error::from("Invalid decimal: overflow from too many digits"))
897 );
898 }
899
900 #[test]
901 fn from_str_overflow_2() {
902 assert!(
903 parse_str_radix_10("99999_99999_99999_99999_99999_11111.11111").is_err(),
904 );
907 }
908
909 #[test]
910 fn from_str_overflow_3() {
911 assert!(
912 parse_str_radix_10("99999_99999_99999_99999_99999_99994").is_err() );
915 }
916
917 #[test]
918 fn from_str_overflow_4() {
919 assert_eq!(
920 parse_str_radix_10("99999_99999_99999_99999_99999_999.99")
923 .unwrap()
924 .unpack(),
925 Decimal::from_i128_with_scale(10_000_000_000_000_000_000_000_000_000, 0).unpack()
926 );
927 }
928
929 #[test]
930 fn from_str_mantissa_overflow_1() {
931 assert_eq!(OVERFLOW_U96, 79_228_162_514_264_337_593_543_950_336);
933 assert_eq!(
934 parse_str_radix_10("79_228_162_514_264_337_593_543_950_33.56")
935 .unwrap()
936 .unpack(),
937 Decimal::from_i128_with_scale(79_228_162_514_264_337_593_543_950_34, 0).unpack()
938 );
939 }
943
944 #[test]
945 fn from_str_mantissa_overflow_2() {
946 assert_eq!(
947 parse_str_radix_10("79_228_162_514_264_337_593_543_950_335.6"),
948 Err(Error::from("Invalid decimal: overflow from mantissa after rounding"))
949 );
950 }
954
955 #[test]
956 fn from_str_mantissa_overflow_3() {
957 assert_eq!(
959 parse_str_radix_10("7.92281625142643375935439503356").unwrap().unpack(),
960 Decimal::from_i128_with_scale(79_228_162_514_264_337_593_543_950_34, 27).unpack()
961 );
962 }
963
964 #[test]
965 fn from_str_mantissa_overflow_4() {
966 assert_eq!(
968 parse_str_radix_10("7.9_228_162_514_264_337_593_543_950_335_6")
969 .unwrap()
970 .unpack(),
971 Decimal::from_i128_with_scale(79_228_162_514_264_337_593_543_950_34, 27).unpack()
972 );
973 }
974
975 #[test]
976 fn invalid_input_1() {
977 assert_eq!(
978 parse_str_radix_10("1.0000000000000000000000000000.5"),
979 Err(Error::from("Invalid decimal: two decimal points"))
980 );
981 }
982
983 #[test]
984 fn invalid_input_2() {
985 assert_eq!(
986 parse_str_radix_10("1.0.5"),
987 Err(Error::from("Invalid decimal: two decimal points"))
988 );
989 }
990
991 #[test]
992 fn character_at_rounding_position() {
993 let tests = [
994 (
996 "1.000_000_000_000_000_000_000_000_000_04",
997 Ok(Decimal::from_i128_with_scale(
998 1_000_000_000_000_000_000_000_000_000_0,
999 28,
1000 )),
1001 ),
1002 (
1003 "1.000_000_000_000_000_000_000_000_000_06",
1004 Ok(Decimal::from_i128_with_scale(
1005 1_000_000_000_000_000_000_000_000_000_1,
1006 28,
1007 )),
1008 ),
1009 (
1011 "1_000_000_000_000_000_000_000_000_000_0.4",
1012 Ok(Decimal::from_i128_with_scale(
1013 1_000_000_000_000_000_000_000_000_000_0,
1014 0,
1015 )),
1016 ),
1017 (
1018 "1_000_000_000_000_000_000_000_000_000_0.6",
1019 Ok(Decimal::from_i128_with_scale(
1020 1_000_000_000_000_000_000_000_000_000_1,
1021 0,
1022 )),
1023 ),
1024 (
1026 "1.000_000_000_000_000_000_000_000_000_0_4",
1027 Ok(Decimal::from_i128_with_scale(
1028 1_000_000_000_000_000_000_000_000_000_0,
1029 28,
1030 )),
1031 ),
1032 (
1033 "1.000_000_000_000_000_000_000_000_000_0_6",
1034 Ok(Decimal::from_i128_with_scale(
1035 1_000_000_000_000_000_000_000_000_000_1,
1036 28,
1037 )),
1038 ),
1039 (
1041 "1.000_000_000_000_000_000_000_000_000_0__4",
1042 Ok(Decimal::from_i128_with_scale(
1043 1_000_000_000_000_000_000_000_000_000_0,
1044 28,
1045 )),
1046 ),
1047 (
1048 "1.000_000_000_000_000_000_000_000_000_0__6",
1049 Ok(Decimal::from_i128_with_scale(
1050 1_000_000_000_000_000_000_000_000_000_1,
1051 28,
1052 )),
1053 ),
1054 (
1055 "1.234567890123456789012345678_9",
1056 Ok(Decimal::from_i128_with_scale(12345678901234567890123456789, 28)),
1057 ),
1058 (
1059 "0.234567890123456789012345678_9",
1060 Ok(Decimal::from_i128_with_scale(2345678901234567890123456789, 28)),
1061 ),
1062 (
1063 "0.1234567890123456789012345678_9",
1064 Ok(Decimal::from_i128_with_scale(1234567890123456789012345679, 28)),
1065 ),
1066 (
1067 "0.1234567890123456789012345678_4",
1068 Ok(Decimal::from_i128_with_scale(1234567890123456789012345678, 28)),
1069 ),
1070 ];
1071
1072 for (input, expected) in tests.iter() {
1073 assert_eq!(parse_str_radix_10(input), *expected, "Test input {}", input);
1074 }
1075 }
1076
1077 #[test]
1078 fn from_str_edge_cases_1() {
1079 assert_eq!(parse_str_radix_10(""), Err(Error::from("Invalid decimal: empty")));
1080 }
1081
1082 #[test]
1083 fn from_str_edge_cases_2() {
1084 assert_eq!(
1085 parse_str_radix_10("0.1."),
1086 Err(Error::from("Invalid decimal: two decimal points"))
1087 );
1088 }
1089
1090 #[test]
1091 fn from_str_edge_cases_3() {
1092 assert_eq!(
1093 parse_str_radix_10("_"),
1094 Err(Error::from("Invalid decimal: must start lead with a number"))
1095 );
1096 }
1097
1098 #[test]
1099 fn from_str_edge_cases_4() {
1100 assert_eq!(
1101 parse_str_radix_10("1?2"),
1102 Err(Error::from("Invalid decimal: unknown character"))
1103 );
1104 }
1105
1106 #[test]
1107 fn from_str_edge_cases_5() {
1108 assert_eq!(
1109 parse_str_radix_10("."),
1110 Err(Error::from("Invalid decimal: no digits found"))
1111 );
1112 }
1113
1114 #[test]
1115 fn from_str_edge_cases_6() {
1116 assert_eq!(
1118 parse_str_radix_10("79_228_162_514_264_337_593_543_950_335.99999"),
1119 Err(Error::from("Invalid decimal: overflow from mantissa after rounding"))
1120 );
1121 }
1122
1123 #[test]
1124 fn to_scientific_0() {
1125 #[cfg(not(feature = "std"))]
1126 use alloc::format;
1127
1128 let dec_zero = format!("{:e}", Decimal::ZERO);
1129 let int_zero = format!("{:e}", 0_u32);
1130 assert_eq!(dec_zero, int_zero);
1131 assert_eq!(
1132 Decimal::from_scientific(&dec_zero)
1133 .unwrap_or_else(|e| panic!("Can't parse {dec_zero} into Decimal: {e:?}")),
1134 Decimal::ZERO
1135 );
1136 assert_eq!(dec_zero, "0e0");
1137 }
1138}