Rust 溢出时带边界的整数运算

Kir*_*hou 2 integer-overflow panic rust

我最近遇到的问题需要根据整数类型的位进行带边界的整数运算。

例如,使用i32整数进行add运算,这里有一段伪代码来表达这个想法:

sum = a + b
max(min(sum, 2147483647), -2147483648)

// if the sum is larger than 2147483647, then return 2147483647.
// if the sum is smaller than -2147483648, then return -2147483648.
Run Code Online (Sandbox Code Playgroud)

为了实现这一点,我天真地写了以下丑陋的代码:

sum = a + b
max(min(sum, 2147483647), -2147483648)

// if the sum is larger than 2147483647, then return 2147483647.
// if the sum is smaller than -2147483648, then return -2147483648.
Run Code Online (Sandbox Code Playgroud)

代码运行良好;但是我的六感告诉我使用类型转换是有问题的。因此,我尝试使用传统的恐慌(异常)处理来解决这个问题……但我坚持使用下面的代码(恐慌结果无法检测下溢或溢出):

fn i32_add_handling_by_casting(a: i32, b: i32) -> i32 {
    let sum: i32;
    if (a as i64 + b as i64) > 2147483647 as i64 {
        sum = 2147483647;
    } else if (a as i64 + b as i64) < -2147483648 as i64 {
        sum = -2147483648;
    } else {
        sum = a + b;
    }
    sum
}

fn main() {
    println!("{:?}", i32_add_handling_by_casting(2147483647, 1));
    println!("{:?}", i32_add_handling_by_casting(-2147483648, -1));
}
Run Code Online (Sandbox Code Playgroud)

总结一下,我有3个问题:

  1. 我的类型转换解决方案对强类型语言有效吗?(如果可能,我需要解释为什么它有效或无效。)
  2. 有没有其他更好的方法来处理这个问题?
  3. 恐慌可以分别处理不同的异常吗?

L. *_* F. 8

在这种情况下,Rust 标准库有一个名为 的方法saturating_add,它支持您的用例:

assert_eq!(10_i32.saturating_add(20), 30);
assert_eq!(i32::MIN.saturating_add(-1), i32::MIN);
assert_eq!(i32::MAX.saturating_add(1), i32::MAX);
Run Code Online (Sandbox Code Playgroud)

在内部,它被实现为编译器内在的


通常,此类问题不打算通过 panic 和 unwinding 来解决,它们仅用于在特殊情况下进行清理。手写版本可能涉及类型转换,但a as i64 + b as i64只计算一次。或者,这是一个使用 的版本checked_add,它None在溢出时返回而不是恐慌:

fn saturating_add(a: i32, b: i32) -> i32 {
    if let Some(sum) = a.checked_add(b) {
        sum
    } else if a < 0 {
        i32::MIN
    } else {
        i32::MAX
    }
}
Run Code Online (Sandbox Code Playgroud)