diff --git a/src/ops/add.rs b/src/ops/add.rs index 52ba675..9a0c5bd 100644 --- a/src/ops/add.rs +++ b/src/ops/add.rs @@ -1,4 +1,6 @@ -use crate::constants::{MAX_I32_SCALE, POWERS_10, SCALE_MASK, SCALE_SHIFT, SIGN_MASK, U32_MASK, U32_MAX}; +use crate::constants::{ + MAX_I32_SCALE, MAX_PRECISION_U32, POWERS_10, SCALE_MASK, SCALE_SHIFT, SIGN_MASK, U32_MASK, U32_MAX, +}; use crate::decimal::{CalculationResult, Decimal}; use crate::ops::common::{Buf24, Dec64}; @@ -261,7 +263,7 @@ fn unaligned_add( rescale_factor -= MAX_I32_SCALE; - if tmp64 > U32_MAX { + if tmp64 > U32_MAX || scale > MAX_PRECISION_U32 { break; } else { high = tmp64 as u32; diff --git a/tests/decimal_tests.rs b/tests/decimal_tests.rs index fd1d677..1eca9b1 100644 --- a/tests/decimal_tests.rs +++ b/tests/decimal_tests.rs @@ -4746,4 +4746,38 @@ mod issues { assert!(c.is_some()); assert_eq!("-429391.87200000000002327170816", c.unwrap().to_string()) } + + #[test] + #[cfg(not(feature = "legacy-ops"))] // I will deprecate this feature/behavior in an upcoming release + fn issue_618_rescaling_overflow() { + fn assert_result(scale: u32, v1: Decimal, v2: Decimal) { + assert_eq!(scale, v1.scale(), "initial scale: {scale}"); + let result1 = v1 + -v2; + assert_eq!( + result1.to_string(), + "-0.0999999999999999999999999999", + "a + -b : {scale}" + ); + assert_eq!(28, result1.scale(), "a + -b : {scale}"); + let result2 = v1 - v2; + assert_eq!( + result2.to_string(), + "-0.0999999999999999999999999999", + "a - b : {scale}" + ); + assert_eq!(28, result2.scale(), "a - b : {scale}"); + } + + let mut a = Decimal::from_str("0.0000000000000000000000000001").unwrap(); + let b = Decimal::from_str("0.1").unwrap(); + assert_result(28, a, b); + + // Try at a new scale (this works) + a.rescale(30); + assert_result(30, a, b); + + // And finally the scale causing an issue + a.rescale(29); + assert_result(29, a, b); + } }