C++关于乘法溢出的判断
概述
首先我们对于乘法溢出的判断,先写测试用例:
由上图我们简化测试用例:
我们可以这样设计乘法溢出函数:
/// 判断两入参相乘是否溢出,溢出返回true,否则返回false
bool is_multi_overflow(int x, int y) {
if (x > 0 && y > 0) {
/// 同为正号
return x > INT_MAX/y;
} else if (x < 0 && y < 0) {
/// 同为负号
if (y == INT_MIN && x <= -1) {
return true;
}
return x < INT_MIN/-y;
} else if (x > 0 && y<0 || (x < 0 && y > 0)) {
/// 异号的情况稍等补上
return false;
} else {
return false;
}
}
接下来我们添加测试用例
#include <cassert>
#include <iostream>
#include <numeric>
/// 判断两入参相乘是否溢出,溢出返回true,否则返回false
bool is_multi_overflow(int x, int y) {
if (x > 0 && y > 0) {
/// 同为正号
return x > INT_MAX/y;
} else if (x < 0 && y < 0) {
/// 同为负号
if (y == INT_MIN && x <= -1) {
return true;
}
return x < INT_MIN/-y;
} else if (x > 0 && y<0 || (x < 0 && y > 0)) {
/// 异号的情况稍等补上
return false;
} else {
return false;
}
}
int main() {
int x = 0;
int y = 0;
int max_num = std::numeric_limits<int>::max();
int min_num = std::numeric_limits<int>::min();
/// case 1 #1
x = 7;
y = 1 + max_num / x;
assert(is_multi_overflow(x, y));
x = max_num - 1;
y = 1 + max_num / x;
assert(is_multi_overflow(x, y));
/// case 1 #2
x = 7;
y = max_num / x;
assert(!is_multi_overflow(x, y));
x = max_num - 1;
y = max_num / x;
assert(!is_multi_overflow(x, y));
/// case 2 #1
x = -7;
y = -1 + min_num / -x;
assert(is_multi_overflow(x, y));
x = min_num + 1;
y = -1 + min_num / -x;
assert(is_multi_overflow(x, y));
/// case 2 #2
x = -7;
y = min_num / -x;
assert(!is_multi_overflow(x, y));
x = min_num + 1;
y = min_num / -x;
assert(!is_multi_overflow(x, y));
}
接下来为特殊数值来添加判断:
添加异号情况的判断:
把函数改为模板,一并添加测试用例:
#include <cassert>
#include <iostream>
#include <numeric>
/// 判断两入参相乘是否溢出,溢出返回true,否则返回false
template <typename T1, typename T2>
bool is_multi_overflow(T1 x, T2 y) {
static_assert(std::is_same<T1, T2>::value,
"is_multi_overflow need same type!");
static_assert(std::is_integral<T1>::value,
" is_multi_overflow need integral type!");
int num_max = std::numeric_limits<T1>::max();
int num_min = std::numeric_limits<T1>::min();
if (x == 0 || y == 0 || x == 1 || y == 1) {
return false;
}
if (x == -1) {
return y == num_min;
} else if (y == -1) {
return x == num_min;
}
if (x > 0 && y > 0) {
/// 同为正号
return x > num_max / y;
} else if (x < 0 && y < 0) {
/// 同为负号
if (y == num_min && x <= -1) {
return true;
}
return x < num_min / -y;
} else if (x > 0 && y < 0 || (x < 0 && y > 0)) {
/// 异号的情况
if (x > y) {
std::swap(x, y);
}
return x < num_min / y;
} else {
return false;
}
}
int main() {
int x = 0;
int y = 0;
int max_num = std::numeric_limits<int>::max();
int min_num = std::numeric_limits<int>::min();
/// case 1 #1
x = 7;
y = 1 + max_num / x;
assert(is_multi_overflow(x, y));
x = max_num - 1;
y = 1 + max_num / x;
assert(is_multi_overflow(x, y));
/// case 1 #2
x = 7;
y = max_num / x;
assert(!is_multi_overflow(x, y));
x = max_num - 1;
y = max_num / x;
assert(!is_multi_overflow(x, y));
/// case 2 #1
x = -7;
y = -1 + min_num / -x;
assert(is_multi_overflow(x, y));
x = min_num + 1;
y = -1 + min_num / -x;
assert(is_multi_overflow(x, y));
/// case 2 #2
x = -7;
y = min_num / -x;
assert(!is_multi_overflow(x, y));
x = min_num + 1;
y = min_num / -x;
assert(!is_multi_overflow(x, y));
/// case 3
x = 0;
y = max_num;
assert(!is_multi_overflow(x, y));
x = min_num;
y = 0;
assert(!is_multi_overflow(x, y));
x = 0;
y = 0;
assert(!is_multi_overflow(x, y));
/// case 4
x = 1;
y = max_num;
assert(!is_multi_overflow(x, y));
x = INT_MIN;
y = 1;
assert(!is_multi_overflow(x, y));
/// case 5
x = -1;
y = max_num;
assert(!is_multi_overflow(x, y));
x = -1;
y = min_num;
assert(is_multi_overflow(x, y));
/// case 6
x = 2;
y = min_num / 2;
assert(!is_multi_overflow(x, y));
assert(!is_multi_overflow(y, x));
x = 2;
y = -1 + min_num / 2;
assert(is_multi_overflow(x, y));
assert(is_multi_overflow(y, x));
}
最后附上完整测试用例:
后记
我们既然有了判断乘法溢出的函数,我们可以借此封装一个带有检查溢出的乘法函数。
#include <optional>
template <typename T1, typename T2>
std::optional<T1> multiplies_s(const T1 x, const T2 y) noexcept {
static_assert(std::is_same<T1, T2>::value, "Multiplies_s need same type!");
static_assert(std::is_integral<T1>::value,
"Multiplies_s need integral type!");
if (is_multi_overflow(x, y)) return {};
return x * y;
}
int main()
{
int x = 5;
int y = 5;
int result = multiplies_s(x, y).value_or(0);
}