C语言 如何检查算术运算是否会溢出?



我是从看Rust和我如何检测无符号整数乘法溢出来的?在Rust中,他们有checked_add,它是这样实现的:

pub const fn checked_add(self, rhs: Self) -> Option<Self> {
  let (a, b) = self.overflowing_add(rhs);
  if unlikely!(b) {None} else {Some(a)}
}
pub const fn overflowing_add(self, rhs: Self) -> (Self, bool) {
  let (a, b) = intrinsics::add_with_overflow(self as $ActualT, rhs as $ActualT);
  (a as Self, b)
}
// can't find where this is implemented...
#[rustc_const_stable(feature = "const_int_overflow", since = "1.40.0")]
pub fn add_with_overflow<T: Copy>(x: T, y: T) -> (T, bool);

如果你尝试将两个u8大整数相加,编译器不允许你:

fn main() {
  let a: u8 = 255;
  let b: u8 = 255;
  let c = a + b;
  // ^^^^^ attempt to compute `u8::MAX + u8::MAX`, which would overflow
}

这是如何在C中完成的?如何在JavaScript中模拟这类事情?

例如,在JavaScript中。我猜在JavaScript中,你会检查它是否击中Infinity,如Number.MAX_VALUE * 2 == Infinity。但是在Rust的情况下,我如何使用特定的低级int数据类型来模拟这个(或在C中)?(没有诉诸checked_add助手方法已经解决了它)。基本上,我想知道如果数据类型不允许溢出,你如何判断它是否会溢出。

我正在努力建立一个编程语言,所以想知道这是如何实现的。

具体来说,我现在要做的是,如果你有一个叫做get_next_power_of_2(u8)的函数,它应该返回一个u8,如果它适合,否则一个u16。但并非所有情况下都是u16。所以我想知道如何首先检查它是否会溢出,如果溢出,转换为更高的int。

这是如何在C中完成的?

当更广泛的数学可用时,代码可以使用它。

int a,b;
...
int_twice_as_wide product = (int_twice_as_wide) a * b;
if (product < INT_MIN || product > INT_MAX) Handle_Overflow();

对于有符号整数数学,溢出是未定义行为 (UB),所以当更广泛的数学不容易获得时,代码可以执行+ - * /的各种预测试,如这里所示。

对于unsigned math,溢出"(模块UINT_MAX + 1)。这对+ -(下图)来说很容易。分部只需要监视/ 0

unsigned a,b;
...
unsigned sum = a + b;
if (sum < a) Handle_Overflow();
无符号*很像上面引用的有符号*代码,但测试更少。
bool is_undefined_umult1(unsigned a, unsigned b) {
  if (b > 0) {
    return a > UINT_MAX / b; 
  }
  return false;
}

如果合适,应该返回u8,否则返回u16。但并非所有情况下都是u16。

C函数不会根据值返回不同的类型。而不是考虑:

uint16_t unt8_mul(unt8_t a, unt8_t b) {
  return (uint16_t) a * b;
} 

对于u8 * u8,避免使用a * b,因为在16位系统上乘法是带符号的,乘积可能溢出并导致UB。

如果你知道变量的大小,你也知道最大&它们可以存储的最小值。在C语言中,您将检查反向操作是否有效,例如:

#include <stdio.h>
int main()
{
    // max value a 4-byte unsigned int can store is 0xFFFFFFFF
    unsigned int UINT32_MAX = 0xFFFFFFFF;
    // var_1 + var_2 would overflow, 
    // as an unsigned int can't hold 0x100000028 (0xFFFFFFFE + 0x2A)
    unsigned int var_1 = 0xFFFFFFFE;
    unsigned int var_2 = 0x2A;
    // check the inverse operation, which can be stored in memory
    // var_1 + var_2 > UINT32_MAX --> UINT32_MAX - var_2 < var_1
    if (UINT32_MAX - var_2 < var_1) {
        printf("Overflown");
    } else {
        printf("No overflown");
    }
    return 0;
}

最新更新