1use std::cmp::Ordering;
54use std::fmt;
55use std::hash::{Hash, Hasher};
56use std::str::FromStr;
57
58use serde::{Deserialize, Deserializer, Serialize, Serializer};
59
60use crate::encoding::DecimalError;
61use crate::Decimal;
62
63pub const MAX_DECIMAL64_PRECISION: u32 = 16;
65
66pub const MAX_DECIMAL64_SCALE: u8 = 18;
68
69const SCALE_NEG_INFINITY: u8 = 253;
71const SCALE_POS_INFINITY: u8 = 254;
72const SCALE_NAN: u8 = 255;
73
74const VALUE_BITS: u32 = 56;
76const MAX_VALUE: i64 = (1i64 << (VALUE_BITS - 1)) - 1; const MIN_VALUE: i64 = -(1i64 << (VALUE_BITS - 1)); #[derive(Clone, Copy)]
115pub struct Decimal64 {
116 packed: i64,
118}
119
120impl Decimal64 {
121 pub fn new(s: &str, scale: u8) -> Result<Self, DecimalError> {
141 Self::with_precision_scale(s, None, Some(scale as i32))
142 }
143
144 pub fn with_precision_scale(
163 s: &str,
164 precision: Option<u32>,
165 scale: Option<i32>,
166 ) -> Result<Self, DecimalError> {
167 if let Some(p) = precision {
169 if p > MAX_DECIMAL64_PRECISION {
170 return Err(DecimalError::InvalidFormat(format!(
171 "Precision {} exceeds maximum {} for Decimal64",
172 p, MAX_DECIMAL64_PRECISION
173 )));
174 }
175 if p == 0 {
176 return Err(DecimalError::InvalidFormat(
177 "Precision must be at least 1".to_string(),
178 ));
179 }
180 }
181
182 let scale_val = scale.unwrap_or(0);
183
184 if scale_val > 0 && scale_val as u8 > MAX_DECIMAL64_SCALE {
186 return Err(DecimalError::InvalidFormat(format!(
187 "Scale {} exceeds maximum {} for Decimal64",
188 scale_val, MAX_DECIMAL64_SCALE
189 )));
190 }
191
192 let s = s.trim();
193
194 let lower = s.to_lowercase();
196 match lower.as_str() {
197 "nan" | "-nan" | "+nan" => return Ok(Self::nan()),
198 "infinity" | "inf" | "+infinity" | "+inf" => return Ok(Self::infinity()),
199 "-infinity" | "-inf" => return Ok(Self::neg_infinity()),
200 _ => {}
201 }
202
203 let (is_negative, s) = if let Some(rest) = s.strip_prefix('-') {
205 (true, rest)
206 } else if let Some(rest) = s.strip_prefix('+') {
207 (false, rest)
208 } else {
209 (false, s)
210 };
211
212 let (int_part, frac_part) = if let Some(dot_pos) = s.find('.') {
214 (&s[..dot_pos], &s[dot_pos + 1..])
215 } else {
216 (s, "")
217 };
218
219 let int_part = int_part.trim_start_matches('0');
221 let int_part = if int_part.is_empty() { "0" } else { int_part };
222
223 if scale_val < 0 {
225 return Self::parse_negative_scale(int_part, is_negative, precision, scale_val);
226 }
227
228 let scale_u8 = scale_val as u8;
229
230 let (int_part, frac_digits) = Self::apply_scale(int_part, frac_part, scale_u8 as usize);
232
233 let (final_int, final_frac) =
235 Self::apply_precision(&int_part, &frac_digits, precision, scale_u8 as usize);
236
237 Self::parts_to_packed(&final_int, &final_frac, is_negative, scale_u8)
239 }
240
241 pub fn from_parts(value: i64, scale: u8) -> Result<Self, DecimalError> {
250 if scale > MAX_DECIMAL64_SCALE {
251 return Err(DecimalError::InvalidFormat(format!(
252 "Scale {} exceeds maximum {}",
253 scale, MAX_DECIMAL64_SCALE
254 )));
255 }
256 if !(MIN_VALUE..=MAX_VALUE).contains(&value) {
257 return Err(DecimalError::InvalidFormat(format!(
258 "Value {} doesn't fit in 56 bits (range {} to {})",
259 value, MIN_VALUE, MAX_VALUE
260 )));
261 }
262
263 Ok(Self::pack(value, scale))
264 }
265
266 #[inline]
268 pub const fn from_raw(packed: i64) -> Self {
269 Self { packed }
270 }
271
272 #[inline]
276 pub const fn infinity() -> Self {
277 Self::pack_special(SCALE_POS_INFINITY)
278 }
279
280 #[inline]
282 pub const fn neg_infinity() -> Self {
283 Self::pack_special(SCALE_NEG_INFINITY)
284 }
285
286 #[inline]
290 pub const fn nan() -> Self {
291 Self::pack_special(SCALE_NAN)
292 }
293
294 #[inline]
298 pub const fn raw(&self) -> i64 {
299 self.packed
300 }
301
302 #[inline]
306 pub fn scale(&self) -> u8 {
307 let scale_byte = self.scale_byte();
308 if scale_byte > MAX_DECIMAL64_SCALE {
309 0 } else {
311 scale_byte
312 }
313 }
314
315 #[inline]
319 pub fn value(&self) -> i64 {
320 if self.is_special() {
321 0
322 } else {
323 self.unpack_value()
324 }
325 }
326
327 #[inline]
329 pub fn is_zero(&self) -> bool {
330 !self.is_special() && self.unpack_value() == 0
331 }
332
333 #[inline]
335 pub fn is_negative(&self) -> bool {
336 !self.is_special() && self.unpack_value() < 0
337 }
338
339 #[inline]
341 pub fn is_positive(&self) -> bool {
342 !self.is_special() && self.unpack_value() > 0
343 }
344
345 #[inline]
347 pub fn is_pos_infinity(&self) -> bool {
348 self.scale_byte() == SCALE_POS_INFINITY
349 }
350
351 #[inline]
353 pub fn is_neg_infinity(&self) -> bool {
354 self.scale_byte() == SCALE_NEG_INFINITY
355 }
356
357 #[inline]
359 pub fn is_infinity(&self) -> bool {
360 self.is_pos_infinity() || self.is_neg_infinity()
361 }
362
363 #[inline]
365 pub fn is_nan(&self) -> bool {
366 self.scale_byte() == SCALE_NAN
367 }
368
369 #[inline]
371 pub fn is_special(&self) -> bool {
372 self.scale_byte() > MAX_DECIMAL64_SCALE
373 }
374
375 #[inline]
377 pub fn is_finite(&self) -> bool {
378 !self.is_special()
379 }
380
381 fn format_decimal(&self) -> String {
385 if self.is_neg_infinity() {
387 return "-Infinity".to_string();
388 }
389 if self.is_pos_infinity() {
390 return "Infinity".to_string();
391 }
392 if self.is_nan() {
393 return "NaN".to_string();
394 }
395
396 let value = self.unpack_value();
397 let scale = self.scale_byte() as u32;
398
399 if value == 0 {
400 return "0".to_string();
401 }
402
403 let is_negative = value < 0;
404 let abs_value = value.unsigned_abs();
405
406 if scale == 0 {
407 return if is_negative {
408 format!("-{}", abs_value)
409 } else {
410 abs_value.to_string()
411 };
412 }
413
414 let scale_factor = 10u64.pow(scale);
415 let int_part = abs_value / scale_factor;
416 let frac_part = abs_value % scale_factor;
417
418 let result = if frac_part == 0 {
419 int_part.to_string()
420 } else {
421 let frac_str = format!("{:0>width$}", frac_part, width = scale as usize);
422 let frac_str = frac_str.trim_end_matches('0');
423 format!("{}.{}", int_part, frac_str)
424 };
425
426 if is_negative {
427 format!("-{}", result)
428 } else {
429 result
430 }
431 }
432
433 #[inline]
435 pub fn to_be_bytes(&self) -> [u8; 8] {
436 self.packed.to_be_bytes()
437 }
438
439 #[inline]
441 pub fn from_be_bytes(bytes: [u8; 8]) -> Self {
442 Self {
443 packed: i64::from_be_bytes(bytes),
444 }
445 }
446
447 pub fn to_decimal(&self) -> Decimal {
449 if self.is_neg_infinity() {
450 return Decimal::neg_infinity();
451 }
452 if self.is_pos_infinity() {
453 return Decimal::infinity();
454 }
455 if self.is_nan() {
456 return Decimal::nan();
457 }
458
459 Decimal::from_str(&self.to_string()).expect("Decimal64 string is always valid")
460 }
461
462 pub fn from_decimal(decimal: &Decimal, scale: u8) -> Result<Self, DecimalError> {
464 if decimal.is_nan() {
465 return Ok(Self::nan());
466 }
467 if decimal.is_pos_infinity() {
468 return Ok(Self::infinity());
469 }
470 if decimal.is_neg_infinity() {
471 return Ok(Self::neg_infinity());
472 }
473
474 Self::new(&decimal.to_string(), scale)
475 }
476
477 #[inline]
479 pub const fn min_value() -> Self {
480 Self::pack(MIN_VALUE, 0)
481 }
482
483 #[inline]
485 pub const fn max_value() -> Self {
486 Self::pack(MAX_VALUE, 0)
487 }
488
489 #[inline]
492 const fn pack(value: i64, scale: u8) -> Self {
493 let scale_part = (scale as i64) << VALUE_BITS;
495 let value_part = value & ((1i64 << VALUE_BITS) - 1);
496 Self {
497 packed: scale_part | value_part,
498 }
499 }
500
501 #[inline]
502 const fn pack_special(scale: u8) -> Self {
503 Self {
505 packed: (scale as i64) << VALUE_BITS,
506 }
507 }
508
509 #[inline]
510 fn scale_byte(&self) -> u8 {
511 ((self.packed >> VALUE_BITS) & 0xFF) as u8
512 }
513
514 #[inline]
515 fn unpack_value(&self) -> i64 {
516 let raw = self.packed & ((1i64 << VALUE_BITS) - 1);
518 if raw & (1i64 << (VALUE_BITS - 1)) != 0 {
520 raw | (!0i64 << VALUE_BITS)
522 } else {
523 raw
524 }
525 }
526
527 fn parse_negative_scale(
529 int_part: &str,
530 is_negative: bool,
531 precision: Option<u32>,
532 scale: i32,
533 ) -> Result<Self, DecimalError> {
534 let round_digits = (-scale) as usize;
535
536 if int_part == "0" {
537 return Ok(Self::pack(0, 0));
538 }
539
540 let int_len = int_part.len();
541
542 if int_len <= round_digits {
543 let num_val: u64 = int_part.parse().unwrap_or(0);
545 let rounding_unit = 10u64.pow(round_digits as u32);
546 let half_unit = rounding_unit / 2;
547
548 let result = if num_val >= half_unit {
549 rounding_unit as i64
550 } else {
551 0
552 };
553
554 let value = if is_negative && result != 0 {
555 -result
556 } else {
557 result
558 };
559
560 if !(MIN_VALUE..=MAX_VALUE).contains(&value) {
561 return Err(DecimalError::InvalidFormat(
562 "Value too large for Decimal64".to_string(),
563 ));
564 }
565
566 return Ok(Self::pack(value, 0));
567 }
568
569 let keep_len = int_len - round_digits;
571 let keep_part = &int_part[..keep_len];
572 let round_part = &int_part[keep_len..];
573
574 let first_rounded_digit = round_part.chars().next().unwrap_or('0');
575 let mut result_int: String = keep_part.to_string();
576
577 if first_rounded_digit >= '5' {
578 result_int = add_one_to_string(&result_int);
579 }
580
581 if let Some(p) = precision {
583 let sig_len = result_int.trim_start_matches('0').len();
584 if sig_len > p as usize && p > 0 {
585 let start = result_int.len().saturating_sub(p as usize);
586 result_int = result_int[start..].to_string();
587 }
588 }
589
590 let significant: i64 = result_int.parse().unwrap_or(0);
592 let value = significant
593 .checked_mul(10i64.pow(round_digits as u32))
594 .ok_or_else(|| {
595 DecimalError::InvalidFormat("Value too large for Decimal64".to_string())
596 })?;
597
598 let value = if is_negative { -value } else { value };
599
600 if !(MIN_VALUE..=MAX_VALUE).contains(&value) {
601 return Err(DecimalError::InvalidFormat(
602 "Value too large for Decimal64".to_string(),
603 ));
604 }
605
606 Ok(Self::pack(value, 0))
607 }
608
609 fn apply_scale(int_part: &str, frac_part: &str, scale: usize) -> (String, String) {
611 if scale == 0 {
612 let first_frac = frac_part.chars().next().unwrap_or('0');
613 if first_frac >= '5' {
614 return (add_one_to_string(int_part), String::new());
615 }
616 return (int_part.to_string(), String::new());
617 }
618
619 if frac_part.len() <= scale {
620 let padded = format!("{:0<width$}", frac_part, width = scale);
621 return (int_part.to_string(), padded);
622 }
623
624 let truncated = &frac_part[..scale];
625 let next_digit = frac_part.chars().nth(scale).unwrap_or('0');
626
627 if next_digit >= '5' {
628 let rounded = add_one_to_string(truncated);
629 if rounded.len() > scale {
630 return (add_one_to_string(int_part), "0".repeat(scale));
631 }
632 let padded = format!("{:0>width$}", rounded, width = scale);
633 return (int_part.to_string(), padded);
634 }
635
636 (int_part.to_string(), truncated.to_string())
637 }
638
639 fn apply_precision(
641 int_part: &str,
642 frac_part: &str,
643 precision: Option<u32>,
644 scale: usize,
645 ) -> (String, String) {
646 let Some(p) = precision else {
647 return (int_part.to_string(), frac_part.to_string());
648 };
649
650 let p = p as usize;
651 let max_int_digits = p.saturating_sub(scale);
652
653 let int_part = int_part.trim_start_matches('0');
654 let int_part = if int_part.is_empty() { "0" } else { int_part };
655
656 if int_part.len() > max_int_digits && max_int_digits > 0 {
657 let start = int_part.len() - max_int_digits;
658 return (int_part[start..].to_string(), frac_part.to_string());
659 } else if max_int_digits == 0 && int_part != "0" {
660 return ("0".to_string(), frac_part.to_string());
661 }
662
663 (int_part.to_string(), frac_part.to_string())
664 }
665
666 fn parts_to_packed(
668 int_part: &str,
669 frac_part: &str,
670 is_negative: bool,
671 scale: u8,
672 ) -> Result<Self, DecimalError> {
673 let int_value: i64 = int_part.parse().unwrap_or(0);
674
675 let scale_factor = 10i64.pow(scale as u32);
676
677 let scaled_int = int_value.checked_mul(scale_factor).ok_or_else(|| {
678 DecimalError::InvalidFormat("Value too large for Decimal64".to_string())
679 })?;
680
681 let frac_value: i64 = if frac_part.is_empty() {
682 0
683 } else {
684 frac_part.parse().unwrap_or(0)
685 };
686
687 let value = scaled_int + frac_value;
688 let value = if is_negative && value != 0 {
689 -value
690 } else {
691 value
692 };
693
694 if !(MIN_VALUE..=MAX_VALUE).contains(&value) {
695 return Err(DecimalError::InvalidFormat(
696 "Value too large for Decimal64".to_string(),
697 ));
698 }
699
700 Ok(Self::pack(value, scale))
701 }
702}
703
704fn add_one_to_string(s: &str) -> String {
707 let mut chars: Vec<char> = s.chars().collect();
708 let mut carry = true;
709
710 for c in chars.iter_mut().rev() {
711 if carry {
712 if *c == '9' {
713 *c = '0';
714 } else {
715 *c = char::from_digit(c.to_digit(10).unwrap() + 1, 10).unwrap();
716 carry = false;
717 }
718 }
719 }
720
721 if carry {
722 format!("1{}", chars.iter().collect::<String>())
723 } else {
724 chars.iter().collect()
725 }
726}
727
728impl PartialEq for Decimal64 {
731 fn eq(&self, other: &Self) -> bool {
732 self.packed == other.packed
734 }
735}
736
737impl Eq for Decimal64 {}
738
739impl PartialOrd for Decimal64 {
740 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
741 Some(self.cmp(other))
742 }
743}
744
745impl Ord for Decimal64 {
746 fn cmp(&self, other: &Self) -> Ordering {
747 match (self.is_special(), other.is_special()) {
749 (true, true) => {
750 self.scale_byte().cmp(&other.scale_byte())
753 }
754 (true, false) => {
755 if self.is_neg_infinity() {
757 Ordering::Less
758 } else {
759 Ordering::Greater }
761 }
762 (false, true) => {
763 if other.is_neg_infinity() {
765 Ordering::Greater
766 } else {
767 Ordering::Less }
769 }
770 (false, false) => {
771 let self_scale = self.scale_byte();
773 let other_scale = other.scale_byte();
774 let self_value = self.unpack_value();
775 let other_value = other.unpack_value();
776
777 if self_scale == other_scale {
778 self_value.cmp(&other_value)
780 } else {
781 let max_scale = self_scale.max(other_scale);
783
784 let self_normalized = if self_scale < max_scale {
785 self_value.saturating_mul(10i64.pow((max_scale - self_scale) as u32))
786 } else {
787 self_value
788 };
789
790 let other_normalized = if other_scale < max_scale {
791 other_value.saturating_mul(10i64.pow((max_scale - other_scale) as u32))
792 } else {
793 other_value
794 };
795
796 self_normalized.cmp(&other_normalized)
797 }
798 }
799 }
800 }
801}
802
803impl Hash for Decimal64 {
804 fn hash<H: Hasher>(&self, state: &mut H) {
805 self.packed.hash(state);
806 }
807}
808
809impl fmt::Debug for Decimal64 {
810 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
811 if self.is_nan() {
812 write!(f, "Decimal64(NaN)")
813 } else if self.is_pos_infinity() {
814 write!(f, "Decimal64(Infinity)")
815 } else if self.is_neg_infinity() {
816 write!(f, "Decimal64(-Infinity)")
817 } else {
818 f.debug_struct("Decimal64")
819 .field("value", &self.to_string())
820 .field("scale", &self.scale())
821 .finish()
822 }
823 }
824}
825
826impl fmt::Display for Decimal64 {
827 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
828 write!(f, "{}", self.format_decimal())
829 }
830}
831
832impl Default for Decimal64 {
833 fn default() -> Self {
834 Self::pack(0, 0)
835 }
836}
837
838impl From<i64> for Decimal64 {
839 fn from(value: i64) -> Self {
840 let clamped = value.clamp(MIN_VALUE, MAX_VALUE);
842 Self::pack(clamped, 0)
843 }
844}
845
846impl From<i32> for Decimal64 {
847 fn from(value: i32) -> Self {
848 Self::pack(value as i64, 0)
849 }
850}
851
852impl Serialize for Decimal64 {
854 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
855 where
856 S: Serializer,
857 {
858 serializer.serialize_i64(self.packed)
859 }
860}
861
862impl<'de> Deserialize<'de> for Decimal64 {
863 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
864 where
865 D: Deserializer<'de>,
866 {
867 let packed = i64::deserialize(deserializer)?;
868 Ok(Self::from_raw(packed))
869 }
870}
871
872impl FromStr for Decimal64 {
873 type Err = DecimalError;
874
875 fn from_str(s: &str) -> Result<Self, Self::Err> {
877 let s = s.trim();
878
879 let lower = s.to_lowercase();
881 match lower.as_str() {
882 "nan" | "-nan" | "+nan" => return Ok(Self::nan()),
883 "infinity" | "inf" | "+infinity" | "+inf" => return Ok(Self::infinity()),
884 "-infinity" | "-inf" => return Ok(Self::neg_infinity()),
885 _ => {}
886 }
887
888 let scale = if let Some(dot_pos) = s.find('.') {
890 let after_dot = &s[dot_pos + 1..];
891 after_dot.chars().take_while(|c| c.is_ascii_digit()).count() as u8
893 } else {
894 0
895 };
896
897 Self::new(s, scale.min(MAX_DECIMAL64_SCALE))
898 }
899}
900
901#[cfg(test)]
902mod tests {
903 use super::*;
904
905 #[test]
908 fn test_new_basic() {
909 let d = Decimal64::new("123.45", 2).unwrap();
910 assert_eq!(d.to_string(), "123.45");
911 assert_eq!(d.scale(), 2);
912 assert_eq!(d.value(), 12345);
913
914 let d = Decimal64::new("100", 0).unwrap();
915 assert_eq!(d.to_string(), "100");
916 assert_eq!(d.scale(), 0);
917
918 let d = Decimal64::new("-50.5", 1).unwrap();
919 assert_eq!(d.to_string(), "-50.5");
920 assert_eq!(d.scale(), 1);
921 assert_eq!(d.value(), -505);
922 }
923
924 #[test]
925 fn test_zero() {
926 let d = Decimal64::new("0", 0).unwrap();
927 assert!(d.is_zero());
928 assert!(!d.is_negative());
929 assert!(!d.is_positive());
930 assert!(d.is_finite());
931
932 let d = Decimal64::new("0.00", 2).unwrap();
933 assert!(d.is_zero());
934 assert_eq!(d.scale(), 2);
935 }
936
937 #[test]
938 fn test_from_str() {
939 let d: Decimal64 = "123.456".parse().unwrap();
940 assert_eq!(d.to_string(), "123.456");
941 assert_eq!(d.scale(), 3);
942
943 let d: Decimal64 = "100".parse().unwrap();
944 assert_eq!(d.to_string(), "100");
945 assert_eq!(d.scale(), 0);
946 }
947
948 #[test]
951 fn test_infinity() {
952 let inf = Decimal64::infinity();
953 assert!(inf.is_pos_infinity());
954 assert!(inf.is_infinity());
955 assert!(inf.is_special());
956 assert!(!inf.is_finite());
957 assert_eq!(inf.to_string(), "Infinity");
958
959 let neg_inf = Decimal64::neg_infinity();
960 assert!(neg_inf.is_neg_infinity());
961 assert!(neg_inf.is_infinity());
962 assert_eq!(neg_inf.to_string(), "-Infinity");
963 }
964
965 #[test]
966 fn test_nan() {
967 let nan = Decimal64::nan();
968 assert!(nan.is_nan());
969 assert!(nan.is_special());
970 assert!(!nan.is_finite());
971 assert_eq!(nan.to_string(), "NaN");
972
973 assert_eq!(nan, Decimal64::nan());
975 }
976
977 #[test]
978 fn test_special_from_str() {
979 assert!(Decimal64::from_str("Infinity").unwrap().is_pos_infinity());
980 assert!(Decimal64::from_str("-Infinity").unwrap().is_neg_infinity());
981 assert!(Decimal64::from_str("NaN").unwrap().is_nan());
982 assert!(Decimal64::from_str("inf").unwrap().is_pos_infinity());
983 assert!(Decimal64::from_str("-inf").unwrap().is_neg_infinity());
984 }
985
986 #[test]
989 fn test_ordering_same_scale() {
990 let a = Decimal64::new("100", 0).unwrap();
991 let b = Decimal64::new("200", 0).unwrap();
992 let c = Decimal64::new("-50", 0).unwrap();
993
994 assert!(c < a);
995 assert!(a < b);
996 }
997
998 #[test]
999 fn test_ordering_different_scale() {
1000 let a = Decimal64::new("1.5", 1).unwrap(); let b = Decimal64::new("1.50", 2).unwrap(); assert_eq!(a.cmp(&b), Ordering::Equal);
1005
1006 let c = Decimal64::new("1.51", 2).unwrap();
1007 assert!(a < c);
1008 }
1009
1010 #[test]
1011 fn test_ordering_with_special() {
1012 let neg_inf = Decimal64::neg_infinity();
1013 let neg = Decimal64::new("-1000", 0).unwrap();
1014 let zero = Decimal64::new("0", 0).unwrap();
1015 let pos = Decimal64::new("1000", 0).unwrap();
1016 let inf = Decimal64::infinity();
1017 let nan = Decimal64::nan();
1018
1019 assert!(neg_inf < neg);
1020 assert!(neg < zero);
1021 assert!(zero < pos);
1022 assert!(pos < inf);
1023 assert!(inf < nan);
1024 }
1025
1026 #[test]
1029 fn test_precision_scale() {
1030 let d = Decimal64::with_precision_scale("123.456", Some(5), Some(2)).unwrap();
1031 assert_eq!(d.to_string(), "123.46");
1032
1033 let d = Decimal64::with_precision_scale("12345.67", Some(5), Some(2)).unwrap();
1034 assert_eq!(d.to_string(), "345.67");
1035 }
1036
1037 #[test]
1038 fn test_negative_scale() {
1039 let d = Decimal64::with_precision_scale("12345", None, Some(-2)).unwrap();
1040 assert_eq!(d.to_string(), "12300");
1041
1042 let d = Decimal64::with_precision_scale("12350", None, Some(-2)).unwrap();
1043 assert_eq!(d.to_string(), "12400");
1044 }
1045
1046 #[test]
1049 fn test_roundtrip() {
1050 let values = ["0", "123.45", "-99.99", "1000000", "-1"];
1051
1052 for s in values {
1053 let d: Decimal64 = s.parse().unwrap();
1054 let packed = d.raw();
1055 let restored = Decimal64::from_raw(packed);
1056 assert_eq!(
1057 d.to_string(),
1058 restored.to_string(),
1059 "Roundtrip failed for {}",
1060 s
1061 );
1062 }
1063 }
1064
1065 #[test]
1066 fn test_byte_roundtrip() {
1067 let d = Decimal64::new("123.45", 2).unwrap();
1068 let bytes = d.to_be_bytes();
1069 let restored = Decimal64::from_be_bytes(bytes);
1070 assert_eq!(d, restored);
1071 }
1072
1073 #[test]
1076 fn test_decimal_conversion() {
1077 let d64 = Decimal64::new("123.456", 3).unwrap();
1078 let decimal = d64.to_decimal();
1079 assert_eq!(decimal.to_string(), "123.456");
1080
1081 let d64_back = Decimal64::from_decimal(&decimal, 3).unwrap();
1082 assert_eq!(d64.to_string(), d64_back.to_string());
1083 }
1084
1085 #[test]
1086 fn test_from_parts() {
1087 let d = Decimal64::from_parts(12345, 2).unwrap();
1088 assert_eq!(d.to_string(), "123.45");
1089 assert_eq!(d.value(), 12345);
1090 assert_eq!(d.scale(), 2);
1091 }
1092
1093 #[test]
1096 fn test_precision_too_large() {
1097 assert!(Decimal64::with_precision_scale("1", Some(17), Some(0)).is_err());
1098 }
1099
1100 #[test]
1101 fn test_scale_too_large() {
1102 assert!(Decimal64::new("1", 19).is_err());
1103 }
1104
1105 #[test]
1106 fn test_value_too_large() {
1107 assert!(Decimal64::from_parts(i64::MAX, 0).is_err());
1109 assert!(Decimal64::from_parts(MIN_VALUE - 1, 0).is_err());
1110 }
1111
1112 #[test]
1115 fn test_min_max_values() {
1116 let min = Decimal64::min_value();
1117 let max = Decimal64::max_value();
1118
1119 assert!(min.is_finite());
1120 assert!(max.is_finite());
1121 assert!(min < max);
1122 assert!(Decimal64::neg_infinity() < min);
1123 assert!(max < Decimal64::infinity());
1124 }
1125
1126 #[test]
1127 fn test_hash() {
1128 use std::collections::HashSet;
1129
1130 let mut set = HashSet::new();
1131 set.insert(Decimal64::new("100", 0).unwrap());
1132 set.insert(Decimal64::new("100", 0).unwrap()); set.insert(Decimal64::new("200", 0).unwrap());
1134 set.insert(Decimal64::nan());
1135
1136 assert_eq!(set.len(), 3);
1137 }
1138}