有没有一种通用的方法来检查给定数据类型(uint32、int 等)的上溢或下溢?
我正在做这样的事情:
uint32 a,b,c;
... //initialize a,b,c
if(b < c) {
a -= (c - b)
}
当我在一些迭代后打印 a 时,它会显示一个大数字,例如:4294963846。
有没有一种通用的方法来检查给定数据类型(uint32、int 等)的上溢或下溢?
我正在做这样的事情:
uint32 a,b,c;
... //initialize a,b,c
if(b < c) {
a -= (c - b)
}
当我在一些迭代后打印 a 时,它会显示一个大数字,例如:4294963846。
要检查算术中的上溢/下溢,请检查与原始值相比的结果。
uint32 a,b;
//assign values
uint32 result = a + b;
if (result < a) {
//Overflow
}
对于您的具体情况,检查将是:
if (a > (c-b)) {
//Underflow
}
我想如果我想这样做,我会创建一个模拟数据类型的类,然后手动完成(我想这会很慢)
class MyInt
{
int val;
MyInt(const int&nval){ val = nval;} // cast from int
operator int(){return val;} // cast to int
// then just overload ALL the operators... putting your check in
};
//typedef int sint32;
typedef MyInt sint32;
它可能比这更棘手,你可能不得不使用定义而不是 typedef...
我用指针做了类似的事情来检查内存在哪里被写到了边界之外。非常慢,但确实找到了内存损坏的地方
Cert 对于未定义行为的有符号整数溢出和未定义行为的无符号包装都有很好的参考,它们涵盖了所有运算符。
该文档提供了以下检查代码,用于使用前置条件进行减法中的无符号换行,如下所示:
void func(unsigned int ui_a, unsigned int ui_b) {
unsigned int udiff;
if (ui_a < ui_b){
/* Handle error */
} else {
udiff = ui_a - ui_b;
}
/* ... */
}
并带有后置条件:
void func(unsigned int ui_a, unsigned int ui_b) {
unsigned int udiff = ui_a - ui_b;
if (udiff > ui_a) {
/* Handle error */
}
/* ... */
}
如果你是gcc 5,你可以使用__builtin_sub_overflow
:
__builtin_sub_overflow( ui_a, ui_b, &udiff )
Boost 有一个名为 Safe Numerics 的简洁库。根据您实例化安全模板的方式,库将在发生上溢或下溢时抛出异常。请参阅https://www.boost.org/doc/libs/1_74_0/libs/safe_numerics/doc/html/index.html。
如果有更大(x2 大小)的整数类型可用,我将在这里提出另一种可能的方法。在这种情况下,可以以增加一点计算为代价来防止溢出发生。
// https://gcc.godbolt.org/z/fh9G6Eeah
#include <exception>
#include <limits>
#include <iostream>
using integer_t = uint32_t; // The desired type
using bigger_t = uint64_t; // Bigger type
constexpr integer_t add(const integer_t a, const integer_t b)
{
static_assert(sizeof(bigger_t)>=2*sizeof(integer_t));
constexpr bigger_t SUP = std::numeric_limits<integer_t>::max();
constexpr bigger_t INF = std::numeric_limits<integer_t>::min();
// Using larger type for operation
bigger_t res = static_cast<bigger_t>(a) + static_cast<bigger_t>(b);
// Check overflows
if(res>SUP) throw std::overflow_error("res too big");
else if(res<INF) throw std::overflow_error("res too small");
// Back to the original type
return static_cast<integer_t>(res); // No danger of narrowing here
}
//---------------------------------------------------------------------------
int main()
{
std::cout << add(100,1) << '\n';
std::cout << add(std::numeric_limits<integer_t>::max(),1) << '\n';
}