两个数字之间的距离通常是这样计算的:
long distance(long x, long y)
{
return x > y ? x - y : y - x;
}
但是,对于带符号的x
和y
,这些减法可能会溢出,因此函数可以在C和C ++中调用未定义的行为。
解决该问题的一种方法是使用无符号类型表示结果距离。距离不能为负,因此不需要带符号的类型。最小和最大带符号类型之间的距离应适合相同大小的无符号类型。 (编辑:正如chux回答的那样,这并不是完全正确的假设。)因此,我确实像这样修改了第一个函数:
unsigned long distance(long x, long y)
{
return (x > y) ? (unsigned long)x - (unsigned long)y
: (unsigned long)y - (unsigned long)x;
}
它现在是否以标准合规和可移植的方式正确计算两个带符号的加长线之间的距离?如果没有,那么解决办法是什么?
答案 0 :(得分:8)
现在是否可以以标准的符合标准且可移植的方式正确计算两个带符号的长点之间的距离?
是的。
稀有异常 1 必须使用更广泛的类型。
考虑x > y
x> = 0,y> = 0
以下内容非常正确,因为强制转换不会更改 value 。
(unsigned long)x - (unsigned long)y
x <0,y <0
由于ULONG_MAX + 1
,x,y的值都增加了(unsigned long)
,相减会抵消掉这个值。
// is akin to
((unsigned long)(x + ULONG_MAX + 1) - (unsigned long)(y + ULONG_MAX + 1))
// or
x - y // with unsigned math.
x> = 0,y <0
(unsigned long)y
的值为y + ULONG_MAX + 1
,大于x
。 (假设ULONG_MAX/2 >= LONG_MAX
1 )差异为负。但是 unsigned 数学会回绕,然后加回ULONG_MAX + 1
。
// is akin to
((unsigned long)x - (unsigned long)(y + ULONG_MAX + 1)) + (ULONG_MAX + 1).
// or
x - y // with unsigned math.
x <0,y> = 0
这种情况不能作为x > y
。
1 :即使这种情况非常普遍,C也不指定ULONG_MAX/2 == LONG_MAX
。我仅在很久以前才遇到过这种情况,但它并不适用。那样的话就是ULONG_MAX == LONG_MAX
。 ULONG_MAX/2 == LONG_MAX
如此令人期待,以至于我怀疑现代平台会冒这样的风险。 C确实指定了ULONG_MAX >= LONG_MAX
。
有符号整数类型的非负值范围是的子范围 对应的无符号整数类型,并且每种类型中相同值的表示形式相同。 ... C11dr§6.2.59
代码可以使用以下代码来检测这些稀有平台。
#if ULONG_MAX/2 < LONG_MAX
#error `unsigned long` too narrow. Need new approach.
#endif
答案 1 :(得分:0)
由于对C和C ++定义了无符号的溢出(和下溢),所以当使用2的补码算法时,修改后的函数就很好了。
答案 2 :(得分:-1)
假定sizeof(unsigned long long)> sizeof(unsigned long),可以安全地将其声明为:unsigned long long distance(long x, long y)
,但是由于当今的补码或其他奇异格式编号并不常用(实际上,几乎没有人有机会在这种机器上进行C编码),unsigned long
类型可以容纳所有可能的距离