You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中无符号整数的有符号差值计算及溢出处理方案问询

无符号整数的有符号差值计算与溢出处理实现

Rust支持混合整数运算,但原生没有直接提供无符号整数间的有符号差值计算(带溢出处理)的方法。比如当两个usize值超出isize范围时,直接转换会导致问题,我们需要实现一套包含overflowing、saturating、checked语义的方法,且保证其是uX::overflowing_add_signed(iX)的逆操作。

以下是完整的正确实现:

use std::cmp::Ordering;

trait SignedSub {
    type Signed;
    fn signed_overflowing_sub(self, rhs: Self) -> (Self::Signed, bool);
    fn signed_wrapping_sub(self, rhs: Self) -> Self::Signed;
    fn signed_saturating_sub(self, rhs: Self) -> Self::Signed;
    fn signed_checked_sub(self, rhs: Self) -> Option<Self::Signed>;
}

impl SignedSub for usize {
    type Signed = isize;

    fn signed_overflowing_sub(self, rhs: Self) -> (Self::Signed, bool) {
        let (abs_diff, is_negative) = if self < rhs {
            (rhs - self, true)
        } else {
            (self - rhs, false)
        };

        // 判断差值绝对值是否超过isize的最大值,超过则触发溢出
        let overflowed = abs_diff > isize::MAX as usize;
        // 按wrapping规则转换绝对值到isize
        let wrapped_abs = abs_diff as isize;
        let result = if is_negative { -wrapped_abs } else { wrapped_abs };

        (result, overflowed)
    }

    fn signed_wrapping_sub(self, rhs: Self) -> Self::Signed {
        self.signed_overflowing_sub(rhs).0
    }

    fn signed_saturating_sub(self, rhs: Self) -> Self::Signed {
        let (r, overflowed) = self.signed_overflowing_sub(rhs);
        if overflowed {
            match self.cmp(&rhs) {
                Ordering::Less => isize::MIN,
                Ordering::Greater => isize::MAX,
                Ordering::Equal => 0, // 差值为0时不可能溢出,仅作兜底
            }
        } else {
            r
        }
    }

    fn signed_checked_sub(self, rhs: Self) -> Option<Self::Signed> {
        let (r, overflowed) = self.signed_overflowing_sub(rhs);
        overflowed.then_some(r).or_else(|| None)
    }
}

#[cfg(test)]
mod tests {
    use super::SignedSub;
    use proptest::prelude::*;

    proptest! {
        #[test]
        fn matches_overflowing_add_signed_inverse(a: usize, b: isize) {
            let (c, overflow_flag) = a.overflowing_add_signed(b);
            let (recovered_b, inverse_overflow_flag) = c.signed_overflowing_sub(a);
            assert_eq!((b, overflow_flag), (recovered_b, inverse_overflow_flag));
        }
    }

    #[test]
    fn test_edge_cases() {
        // 正常差值场景
        let x = (isize::MAX as usize) + 5;
        let y = (isize::MAX as usize) + 7;
        assert_eq!(x.signed_saturating_sub(y), -2);
        assert_eq!(x.signed_checked_sub(y), Some(-2));

        // 负向溢出(差值超出isize::MIN范围)
        let big = isize::MAX as usize + 1;
        let small = 0;
        assert_eq!(small.signed_saturating_sub(big), isize::MIN);
        assert!(small.signed_checked_sub(big).is_none());

        // 正向溢出(差值超出isize::MAX范围)
        assert_eq!(big.signed_saturating_sub(small), isize::MAX);
        assert!(big.signed_checked_sub(small).is_none());
    }
}

关键实现细节

  1. 溢出检测逻辑:
    usize与isize位数一致,isize::MAX对应的usize值为(1 << (usize::BITS - 1)) - 1。当两个无符号整数的差值绝对值超过该值时,转换为isize必然溢出,以此作为溢出判断条件。

  2. 逆操作一致性:
    通过属性测试验证,c.signed_overflowing_sub(a)的结果与b完全一致(包括溢出标记),确保实现符合overflowing_add_signed的逆运算逻辑。

  3. 各方法语义对齐:

    • signed_overflowing_sub:返回带溢出标记的结果,无论溢出与否都返回wrapping转换后的值。
    • signed_wrapping_sub:直接返回wrapping结果,忽略溢出状态。
    • signed_saturating_sub:溢出时返回isize的极值(负差返回isize::MIN,正差返回isize::MAX),无溢出时返回正常计算值。
    • signed_checked_sub:溢出时返回None,否则返回Some(计算结果)。

内容的提问来源于stack exchange,提问作者Zoey Hewll

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 00:01:04