CPP 数字安全计算
SafeAdd / SafeMinus
测试代码:https://godbolt.org/z/Gn1bx1d7r
#include <cstdint>
#include <limits>
#include <iostream>
template<typename Type1, typename Type2>
inline static void SafeAdd(Type1& val1, Type2 val2)
{
static_assert(sizeof(val1) <= sizeof(uint64_t));
static_assert(sizeof(val2) <= sizeof(uint64_t));
if (0 == val2)
{
return;
}
if (val2 < 0)
{
std::cout << "val1=" << val1 << " val2=" << val2 << " std::numeric_limits<Type1>::min()=" << std::numeric_limits<Type1>::min() << std::endl;
uint64_t uVal1 = (uint64_t)(val1 - std::numeric_limits<Type1>::min());
std::cout << "uVal1=" << uVal1 << std::endl;
std::cout << "UINT32_MAX=" << UINT32_MAX << std::endl;
std::cout << "(uint32_t)(-val2)=" << (uint32_t)(-val2) << std::endl;
uint64_t uVal2 = 0;
if ((-val2) < UINT32_MAX)
{
uVal2 = (uint32_t)(-val2);
}
else
{
uVal2 = (uint64_t)(-val2);
}
std::cout << "uVal2=" << uVal2 << std::endl;
if (uVal2 > uVal1)
{
val1 = std::numeric_limits<Type1>::min();
return;
}
}
else
{
uint64_t uVal1 = (uint64_t)(std::numeric_limits<Type1>::max() - val1);
uint64_t uVal2 = (uint64_t)(val2);
if (uVal2 > uVal1)
{
val1 = std::numeric_limits<Type1>::max();
return;
}
}
val1 += val2;
}
template<typename Type1, typename Type2>
inline static void SafeMinus(Type1& val1, Type2 val2)
{
static_assert(sizeof(val1) <= sizeof(uint64_t));
static_assert(sizeof(val2) <= sizeof(uint64_t));
if (0 == val2)
{
return;
}
if (val2 < 0)
{
uint64_t uVal2 = 0;
if ((-val2) < UINT32_MAX)
{
uVal2 = (uint32_t)(-val2);
}
else
{
uVal2 = (uint64_t)(-val2);
}
std::cout << "uVal2=" << uVal2 << std::endl;
if (uVal2 > (uint64_t)(std::numeric_limits<Type1>::max() - val1))
{
val1 = std::numeric_limits<Type1>::max();
return;
}
}
else
{
if ((uint64_t)(val2) > (uint64_t)(val1 - std::numeric_limits<Type1>::min()))
{
val1 = std::numeric_limits<Type1>::min();
return;
}
}
val1 -= val2;
}
int main()
{
// 4294967295 = 2^32-1
std::cout << "UINT32_MAX=" << UINT32_MAX << std::endl;
std::cout << "std::numeric_limits<uint32_t>::max()=" << std::numeric_limits<uint32_t>::max() << std::endl;
// 18446744073709551615 = 2^64-1
std::cout << "UINT64_MAX=" << UINT64_MAX << std::endl;
std::cout << "std::numeric_limits<uint64_t>::max()=" << std::numeric_limits<uint64_t>::max() << std::endl;
// 2147483647 = 2^31-1
std::cout << "INT32_MAX=" << INT32_MAX << std::endl;
std::cout << "std::numeric_limits<int32_t>::max()=" << std::numeric_limits<int32_t>::max() << std::endl;
// -2147483648
std::cout << "INT32_MIN=" << INT32_MIN << std::endl;
std::cout << "std::numeric_limits<int32_t>::min()=" << std::numeric_limits<int32_t>::min() << std::endl;
// -2147483648
int32_t min = -2147483648;
std::cout << "min=" << min << std::endl;
// 测试 SafeAdd 函数
uint64_t a = UINT64_MAX;
int32_t b = INT32_MIN;
// 问题:将 b 转换为无符号会溢出,期望是 2147483648 但通过强转后变为 18446744071562067968
std::cout << "(uint64_t)(-b)=" << (uint64_t)(-b) << std::endl;
// a + b =18446744071562067967 符合预期
std::cout << "a + b =" << a + b << std::endl;
// SafeAdd(a, b)=18446744071562067967 符合预期
SafeAdd(a, b);
std::cout << "SafeAdd(a, b)=" << a << std::endl;
// 测试 SafeMinus 函数
int32_t a2 = INT32_MIN;
uint64_t b2 = UINT64_MAX;
// a2 - b2 =18446744071562067969 出现了类型隐式提升,不符合预期
std::cout << "a2 - b2 =" << a2 - b2 << std::endl;
// SafeMinus(a2, b2)=-2147483648 此函数保证 a2 在执行减法后不会比 int32_t 所表示最小的数更小
SafeMinus(a2, b2);
std::cout << "SafeMinus(a2, b2)=" << a2 << std::endl;
return 0;
}
输出:
UINT32_MAX=4294967295
std::numeric_limits<uint32_t>::max()=4294967295
UINT64_MAX=18446744073709551615
std::numeric_limits<uint64_t>::max()=18446744073709551615
INT32_MAX=2147483647
std::numeric_limits<int32_t>::max()=2147483647
INT32_MIN=-2147483648
std::numeric_limits<int32_t>::min()=-2147483648
min=-2147483648
(uint64_t)(-b)=18446744071562067968
a + b =18446744071562067967
val1=18446744073709551615 val2=-2147483648 std::numeric_limits<Type1>::min()=0
uVal1=18446744073709551615
UINT32_MAX=4294967295
(uint32_t)(-val2)=2147483648
uVal2=2147483648
SafeAdd(a, b)=18446744071562067967
a2 - b2 =18446744071562067969
SafeMinus(a2, b2)=-2147483648