Merge rust-bitcoin/rust-bitcoin#2436: Add unchecked variants to Amount and SignedAmount

df1d2f6eb5 Add unchecked variants to Amount and SignedAmount (yancy)

Pull request description:

  The checked variants have worse performance than the unchecked variants due to the additional branching operations.  To improve performance where overflow is either not possible or not a concern, unchecked variants of `Amount` and `SignedAmount` are introduced for addition, subtraction and multiplication.

  Note, it seems the default behavior for the test framework is to panic on overflow, so I haven't figured out a good way to add tests for this.  Marking as a draft for now.

  closes: https://github.com/rust-bitcoin/rust-bitcoin/issues/2434

ACKs for top commit:
  Kixunil:
    ACK df1d2f6eb5
  apoelstra:
    ACK df1d2f6eb5 gonna go ahead and merge this, we can revisit if necessary when we look at `units` overflow behavior in general

Tree-SHA512: 3fbb0ec81a758b350569226c44e25f6ca49e551566bee83c05c1c2b343874ef657d63a36b5f51c41582d8a8e36466275c574ebff6d363ed7c112ac8b4d5376fa
This commit is contained in:
Andrew Poelstra 2024-02-15 14:57:21 +00:00
commit bf29a76d89
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 57 additions and 0 deletions

View File

@ -864,6 +864,21 @@ impl Amount {
/// Returns [None] if overflow occurred. /// Returns [None] if overflow occurred.
pub fn checked_rem(self, rhs: u64) -> Option<Amount> { self.0.checked_rem(rhs).map(Amount) } pub fn checked_rem(self, rhs: u64) -> Option<Amount> { self.0.checked_rem(rhs).map(Amount) }
/// Unchecked addition.
///
///
/// Computes `self + rhs`. Panics in debug mode, wraps in release mode.
pub fn unchecked_add(self, rhs: Amount) -> Amount {
Self(self.0 + rhs.0)
}
/// Unchecked subtraction.
///
/// Computes `self - rhs`. Panics in debug mode, wraps in release mode.
pub fn unchecked_sub(self, rhs: Amount) -> Amount {
Self(self.0 - rhs.0)
}
/// Convert to a signed amount. /// Convert to a signed amount.
pub fn to_signed(self) -> Result<SignedAmount, OutOfRangeError> { pub fn to_signed(self) -> Result<SignedAmount, OutOfRangeError> {
if self.to_sat() > SignedAmount::MAX.to_sat() as u64 { if self.to_sat() > SignedAmount::MAX.to_sat() as u64 {
@ -1237,6 +1252,20 @@ impl SignedAmount {
self.0.checked_rem(rhs).map(SignedAmount) self.0.checked_rem(rhs).map(SignedAmount)
} }
/// Unchecked addition.
///
/// Computes `self + rhs`. Panics in debug mode, wraps in release mode.
pub fn unchecked_add(self, rhs: SignedAmount) -> SignedAmount {
Self(self.0 + rhs.0)
}
/// Unchecked subtraction.
///
/// Computes `self - rhs`. Panics in debug mode, wraps in release mode.
pub fn unchecked_sub(self, rhs: SignedAmount) -> SignedAmount {
Self(self.0 - rhs.0)
}
/// Subtraction that doesn't allow negative [SignedAmount]s. /// Subtraction that doesn't allow negative [SignedAmount]s.
/// Returns [None] if either [self], `rhs` or the result is strictly negative. /// Returns [None] if either [self], `rhs` or the result is strictly negative.
pub fn positive_sub(self, rhs: SignedAmount) -> Option<SignedAmount> { pub fn positive_sub(self, rhs: SignedAmount) -> Option<SignedAmount> {
@ -1934,6 +1963,34 @@ mod tests {
assert_eq!(ssat(-6).checked_div(2), Some(ssat(-3))); assert_eq!(ssat(-6).checked_div(2), Some(ssat(-3)));
} }
#[test]
#[cfg(not(debug_assertions))]
fn unchecked_amount_add() {
let amt = Amount::MAX.unchecked_add(Amount::ONE_SAT);
assert_eq!(amt, Amount::ZERO);
}
#[test]
#[cfg(not(debug_assertions))]
fn unchecked_signed_amount_add() {
let signed_amt = SignedAmount::MAX.unchecked_add(SignedAmount::ONE_SAT);
assert_eq!(signed_amt, SignedAmount::MIN);
}
#[test]
#[cfg(not(debug_assertions))]
fn unchecked_amount_subtract() {
let amt = Amount::ZERO.unchecked_sub(Amount::ONE_SAT);
assert_eq!(amt, Amount::MAX);
}
#[test]
#[cfg(not(debug_assertions))]
fn unchecked_signed_amount_subtract() {
let signed_amt = SignedAmount::MIN.unchecked_sub(SignedAmount::ONE_SAT);
assert_eq!(signed_amt, SignedAmount::MAX);
}
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
#[test] #[test]
fn floating_point() { fn floating_point() {