我是从看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;
}