Skip to content

Commit

Permalink
Fix Decimal::to_i64 for I64::MIN (#496)
Browse files Browse the repository at this point in the history
Co-authored-by: Paul Mason <paul@paulmason.me>
Co-authored-by: Arthur Silva <arthurprs@gmail.com>
  • Loading branch information
paupino and arthurprs committed Mar 19, 2022
1 parent 168fa67 commit 2de2a6d
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 13 deletions.
19 changes: 15 additions & 4 deletions src/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2203,15 +2203,26 @@ fn base2_to_decimal(
impl ToPrimitive for Decimal {
fn to_i64(&self) -> Option<i64> {
let d = self.trunc();
// Quick overflow check
if d.hi != 0 || (d.mid & 0x8000_0000) > 0 {
// If it is in the hi bit then it is a clear overflow.
if d.hi != 0 {
// Overflow
return None;
}
let negative = self.is_sign_negative();

// A bit more convoluted in terms of checking when it comes to the hi bit due to twos-complement
if d.mid & 0x8000_0000 > 0 {
if negative && d.mid == 0x8000_0000 && d.lo == 0 {
// We do this because below we try to convert the i64 to a positive first - of which
// doesn't fit into an i64.
return Some(i64::MIN);
}
return None;
}

let raw: i64 = (i64::from(d.mid) << 32) | i64::from(d.lo);
if self.is_sign_negative() {
Some(-raw)
if negative {
Some(raw.neg())
} else {
Some(raw)
}
Expand Down
29 changes: 20 additions & 9 deletions tests/decimal_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2633,15 +2633,26 @@ fn it_converts_to_f64_try() {

#[test]
fn it_converts_to_i64() {
assert_eq!(5i64, Decimal::from_str("5").unwrap().to_i64().unwrap());
assert_eq!(-5i64, Decimal::from_str("-5").unwrap().to_i64().unwrap());
assert_eq!(5i64, Decimal::from_str("5.12345").unwrap().to_i64().unwrap());
assert_eq!(-5i64, Decimal::from_str("-5.12345").unwrap().to_i64().unwrap());
assert_eq!(
0x7FFF_FFFF_FFFF_FFFF,
Decimal::from_str("9223372036854775807").unwrap().to_i64().unwrap()
);
assert_eq!(None, Decimal::from_str("92233720368547758089").unwrap().to_i64());
let tests = [
("5", Some(5_i64)),
("-5", Some(-5_i64)),
("5.12345", Some(5_i64)),
("-5.12345", Some(-5_i64)),
("-9223372036854775808", Some(-9223372036854775808_i64)),
("-9223372036854775808", Some(i64::MIN)),
("9223372036854775807", Some(9223372036854775807_i64)),
("9223372036854775807", Some(i64::MAX)),
("-9223372036854775809", None), // i64::MIN - 1
("9223372036854775808", None), // i64::MAX + 1
// Clear overflows in hi bit
("-92233720368547758089", None),
("92233720368547758088", None),
];
for (input, expected) in tests {
let input = Decimal::from_str(input).unwrap();
let actual = input.to_i64();
assert_eq!(expected, actual, "Input: {}", input);
}
}

#[test]
Expand Down

0 comments on commit 2de2a6d

Please sign in to comment.