我想计算两个整数的绝对差值。天真地,这只是abs(a - b)
。但是,这有两个问题,具体取决于整数是有符号还是无符号:
对于无符号整数,如果a - b
大于b
,a
将是一个大的正数,绝对值操作将无法解决此问题。
对于有符号整数,a - b
可能超出可以表示为有符号整数的值范围,从而导致未定义的行为。 (显然,这意味着结果必须是无符号整数。)
如何以有效的方式解决这些问题?
我想要一个适用于有符号和无符号整数的算法(或算法对),并且不依赖于将值转换为更大的整数。
(另外,澄清一下:我的问题不是如何在代码中写这个,而是我应该写什么才能保证溢出安全。将abs(a - b)
计算为有符号值然后转换为无符号值不起作用,因为有符号整数溢出通常是未定义的操作,至少在C中。)
答案 0 :(得分:7)
(我在提出这个问题之后一直在自己解决这个问题 - 我认为这会更难,如果有更好的答案,我还是会欢迎其他答案!)
无符号整数的解决方案相对简单(如Jack Toole的回答所述),并且通过在减法之外移动(隐含的)条件来工作,这样我们总是从较大的数字中减去较小的数字,而不是比较可能包装的值为零:
if (a > b)
return a - b;
else
return b - a;
这就留下了有符号整数的问题。请考虑以下变体:
if (a > b)
return (unsigned) a - (unsigned) b;
else
return (unsigned) b - (unsigned) a;
我们可以通过使用模运算来轻松证明这是有效的。我们知道a
和(unsigned) a
是一致的模UINT_MAX
。此外,无符号整数减法运算与实际减法模UINT_MAX
一致,因此结合这些事实,我们知道(unsigned) a - (unsigned) b
与a - b
模UINT_MAX
的实际值一致。a - b
}。最后,因为我们知道0
的实际值必须介于UINT_MAX-1
和{{1}}之间,所以我们知道这是完全相等的。
答案 1 :(得分:1)
编辑使用Brook Moses对签名类型的修复和Kerrek SB的make_unsigned允许 模板使用。
首先,你可以为make_unsigned提供以下内容,或者你可以使用std :: make_unsigned,tr1 :: make_unsigned或BOOST :: make_unsigned。
template <typename T> struct make_unsigned { };
template <> struct make_unsigned<bool > {};
template <> struct make_unsigned< signed short > {typedef unsigned short type;};
template <> struct make_unsigned<unsigned short > {typedef unsigned short type;};
template <> struct make_unsigned< signed int > {typedef unsigned int type;};
template <> struct make_unsigned<unsigned int > {typedef unsigned int type;};
template <> struct make_unsigned< signed long > {typedef unsigned long type;};
template <> struct make_unsigned<unsigned long > {typedef unsigned long type;};
template <> struct make_unsigned< signed long long> {typedef unsigned long long type;};
template <> struct make_unsigned<unsigned long long> {typedef unsigned long long type;};
然后,模板定义变得简单:
template <typename T>
typename make_unsigned<T>::type absdiff(T a, T b)
{
typedef typename make_unsigned<T>::type UT;
if (a > b)
return static_cast<UT>(a) - static_cast<UT>(b);
else
return static_cast<UT>(b) - static_cast<UT>(a);
}
布鲁克斯摩西解释说,这种环绕是安全的。
答案 2 :(得分:1)
这是一个想法:如果我们没有签名,我们只是采取正确的区别。如果我们签名,我们将返回相应的无符号类型:
#include <type_traits>
template <typename T, bool> struct absdiff_aux;
template <typename T> struct absdiff_aux<T, true>
{
static T absdiff(T a, T b) { return a < b ? b - a : a - b; }
};
template <typename T> struct absdiff_aux<T, false>
{
typedef typename std::make_unsigned<T>::type UT;
static UT absdiff(T a, T b)
{
if ((a > 0 && b > 0) || (a <= 0 && b <= 0))
return { a < b ? b - a : a - b; }
if (b > 0)
{
UT d = -a;
return UT(b) + d
}
else
{
UT d = -b;
return UT(a) + d;
}
}
};
template <typename T> typename std::make_unsigned<T>::type absdiff(T a, T b)
{
return absdiff_aux<T, std::is_signed<T>::value>::absdiff(a, b);
}
用法:int a, b; unsigned int d = absdiff(a, b);
答案 3 :(得分:1)
代码:
#include <stdio.h>
#include <limits.h>
unsigned AbsDiffSigned(int a, int b)
{
unsigned diff = (unsigned)a - (unsigned)b;
if (a < b) diff = 1 + ~diff;
return diff;
}
unsigned AbsDiffUnsigned(unsigned a, unsigned b)
{
unsigned diff = a - b;
if (a < b) diff = 1 + ~diff;
return diff;
}
int testDataSigned[] =
{
{ INT_MIN },
{ INT_MIN + 1 },
{ -1 },
{ 0 },
{ 1 },
{ INT_MAX - 1 },
{ INT_MAX }
};
unsigned testDataUnsigned[] =
{
{ 0 },
{ 1 },
{ UINT_MAX / 2 + 1 },
{ UINT_MAX - 1 },
{ UINT_MAX }
};
int main(void)
{
int i, j;
printf("Absolute difference of signed integers:\n");
for (j = 0; j < sizeof(testDataSigned)/sizeof(testDataSigned[0]); j++)
for (i = 0; i < sizeof(testDataSigned)/sizeof(testDataSigned[0]); i++)
printf("abs(%d - %d) = %u\n",
testDataSigned[j],
testDataSigned[i],
AbsDiffSigned(testDataSigned[j], testDataSigned[i]));
printf("Absolute difference of unsigned integers:\n");
for (j = 0; j < sizeof(testDataUnsigned)/sizeof(testDataUnsigned[0]); j++)
for (i = 0; i < sizeof(testDataUnsigned)/sizeof(testDataUnsigned[0]); i++)
printf("abs(%u - %u) = %u\n",
testDataUnsigned[j],
testDataUnsigned[i],
AbsDiffUnsigned(testDataUnsigned[j], testDataUnsigned[i]));
return 0;
}
输出:
Absolute difference of signed integers:
abs(-2147483648 - -2147483648) = 0
abs(-2147483648 - -2147483647) = 1
abs(-2147483648 - -1) = 2147483647
abs(-2147483648 - 0) = 2147483648
abs(-2147483648 - 1) = 2147483649
abs(-2147483648 - 2147483646) = 4294967294
abs(-2147483648 - 2147483647) = 4294967295
abs(-2147483647 - -2147483648) = 1
abs(-2147483647 - -2147483647) = 0
abs(-2147483647 - -1) = 2147483646
abs(-2147483647 - 0) = 2147483647
abs(-2147483647 - 1) = 2147483648
abs(-2147483647 - 2147483646) = 4294967293
abs(-2147483647 - 2147483647) = 4294967294
abs(-1 - -2147483648) = 2147483647
abs(-1 - -2147483647) = 2147483646
abs(-1 - -1) = 0
abs(-1 - 0) = 1
abs(-1 - 1) = 2
abs(-1 - 2147483646) = 2147483647
abs(-1 - 2147483647) = 2147483648
abs(0 - -2147483648) = 2147483648
abs(0 - -2147483647) = 2147483647
abs(0 - -1) = 1
abs(0 - 0) = 0
abs(0 - 1) = 1
abs(0 - 2147483646) = 2147483646
abs(0 - 2147483647) = 2147483647
abs(1 - -2147483648) = 2147483649
abs(1 - -2147483647) = 2147483648
abs(1 - -1) = 2
abs(1 - 0) = 1
abs(1 - 1) = 0
abs(1 - 2147483646) = 2147483645
abs(1 - 2147483647) = 2147483646
abs(2147483646 - -2147483648) = 4294967294
abs(2147483646 - -2147483647) = 4294967293
abs(2147483646 - -1) = 2147483647
abs(2147483646 - 0) = 2147483646
abs(2147483646 - 1) = 2147483645
abs(2147483646 - 2147483646) = 0
abs(2147483646 - 2147483647) = 1
abs(2147483647 - -2147483648) = 4294967295
abs(2147483647 - -2147483647) = 4294967294
abs(2147483647 - -1) = 2147483648
abs(2147483647 - 0) = 2147483647
abs(2147483647 - 1) = 2147483646
abs(2147483647 - 2147483646) = 1
abs(2147483647 - 2147483647) = 0
Absolute difference of unsigned integers:
abs(0 - 0) = 0
abs(0 - 1) = 1
abs(0 - 2147483648) = 2147483648
abs(0 - 4294967294) = 4294967294
abs(0 - 4294967295) = 4294967295
abs(1 - 0) = 1
abs(1 - 1) = 0
abs(1 - 2147483648) = 2147483647
abs(1 - 4294967294) = 4294967293
abs(1 - 4294967295) = 4294967294
abs(2147483648 - 0) = 2147483648
abs(2147483648 - 1) = 2147483647
abs(2147483648 - 2147483648) = 0
abs(2147483648 - 4294967294) = 2147483646
abs(2147483648 - 4294967295) = 2147483647
abs(4294967294 - 0) = 4294967294
abs(4294967294 - 1) = 4294967293
abs(4294967294 - 2147483648) = 2147483646
abs(4294967294 - 4294967294) = 0
abs(4294967294 - 4294967295) = 1
abs(4294967295 - 0) = 4294967295
abs(4294967295 - 1) = 4294967294
abs(4294967295 - 2147483648) = 2147483647
abs(4294967295 - 4294967294) = 1
abs(4294967295 - 4294967295) = 0