你如何检查算术运算是否会溢出?

How do you check if an arithmetic operation will overflow?

我来自于 Rust 和 How do I detect unsigned integer multiply overflow?,在 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 的情况下,我如何使用特定的低级 uint 数据类型来模拟它(或在 C 中)? (无需求助于已经解决它的 checked_add 辅助方法)。基本上我想知道如果数据类型不允许您溢出,您如何判断它是否会溢出。

我正在构建一种编程语言,所以想知道它是如何实现的。

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

How is this done in 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),因此当更广泛的数学运算不容易获得时,代码可以执行各种pre-tests 对于 + - * / 完成 here.

对于无符号数学,溢出“环绕”(一个模块UINT_MAX + 1)。 + -(下)很容易。除法只需要关注/ 0.

unsigned a,b;
...
unsigned sum = a + b;
if (sum < a) Handle_Overflow();

unsigned * 与上面引用的 signed * 代码非常相似,但测试较少。

bool is_undefined_umult1(unsigned a, unsigned b) {
  if (b > 0) {
    return a > UINT_MAX / b; 
  }
  return false;
}

it should return a u8 if it fits, otherwise a u16. But not a u16 in all cases.

C 函数不会 return 根据值的不同类型。相反考虑:

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("Overflow\n");
    } else {
        printf("No overflow\n");
    }
    return 0;
}