【问题标题】:distance between two signed numbers两个有符号数之间的距离
【发布时间】:2018-09-30 14:32:32
【问题描述】:

两个数字之间的距离通常是这样计算的:

long distance(long x, long y)
{
     return x > y ? x - y : y - x;
}

但是,使用带符号的 xy 这些减法可能会溢出,因此该函数可以在 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;
}

它现在是否以符合标准且可移植的方式正确计算两个有符号长整数之间的距离?如果没有,解决办法是什么?

【问题讨论】:

  • 选择一个,C 或 C++。两者的答案可能相同,但您应该分别询问,因为它们是具有不同规范的不同语言。您可以为每种语言分别输入相同的问题。
  • 如果我错了,请纠正我,但是如果您将两个整数相加而不是减去两个整数,则存在溢出的可能性???
  • 有符号到无符号的转换使用二进制补码,并且定义明确,所以上面应该定义清楚。可悲的是,鉴于当今 C++ 世界完全过度设计的状态,从标准中找到所有相关的权威引用可能需要一天的大部分时间,我需要去买杂货;所以我会说这是很好的定义,然后继续。
  • @MukeshVerma 考虑int x = INT_MIN; int y = INT_MAX; x = y - x;
  • @peter:为什么会出乎意料? LONG_MAX - LONG_MIN 正好是 ULONG_MAX。

标签: c++ c language-lawyer distance


【解决方案1】:

它现在是否以符合标准且可移植的方式正确计算两个有符号长整数之间的距离?

是的。

罕见的例外1 需要使用更宽的类型。


考虑x > y时的3种情况

x >= 0, y >= 0

以下是非常正确的,因为演员表不会改变

(unsigned long)x - (unsigned long)y

x

由于(unsigned long),x,y 值都增加了ULONG_MAX + 1,而减法抵消了这一点。

// is akin to 
((unsigned long)(x + ULONG_MAX + 1) - (unsigned long)(y + ULONG_MAX + 1))
// or
x - y // with unsigned math.

x >= 0, y

(unsigned long)y 的值为y + ULONG_MAX + 1,大于x。 (假设ULONG_MAX/2 >= LONG_MAX1)差异为负。然而 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

这种情况不可能是x > y


1:C 没有指定ULONG_MAX/2 == LONG_MAX,尽管这非常常见。我很久以前才遇到过这种情况,它并不适用。在那种情况下,它是ULONG_MAX == LONG_MAXULONG_MAX/2 == LONG_MAX 是如此令人期待,以至于我怀疑现代平台会冒着不这样做的风险。 C 确实指定了ULONG_MAX >= LONG_MAX

有符号整数类型的非负值范围是 对应的无符号整数类型,并且每种类型中相同值的表示是相同的。 ... C11dr §6.2.5 9

代码可以使用下面的代码来检测这些稀有平台。

#if ULONG_MAX/2 < LONG_MAX
  #error `unsigned long` too narrow.  Need new approach.
#endif

【讨论】:

  • 所以只有在 LONG_MAX - LONG_MIN > ULONG_MAX 的平台上它不起作用?如果这很奇特,那么仅仅静态断言事实并非如此就足够了。
  • @Öö Tiib 是的,_Static_assert(自 C11 起)或宏魔法的一些使用。我想避免LONG_MAX - LONG_MIN 作为理论上,即使uintmax_t 数学也可能溢出。
【解决方案2】:

由于无符号上溢(和下溢)在 C 和 C++ 中定义良好,因此当使用 2 的补码算术时,修改后的函数非常好。

【讨论】:

  • 这在直觉上似乎是正确的,但是你能扩展并证明这个断言吗?
  • 为了证明这一点,我需要证明 2 的补码算术正确性,这已经被证明了。试着找一个反例,你不会找到的。
  • @chqrlie 如果取字节,2 - (-2) = 40x02 - 0xFE = 4(有溢出),(-1) - (-2) = 0xFF - 0xFE = 1long/unsigned long 的逻辑是一样的,但是你有更多的字节要写。
  • @EirNym:我只是要求一个正式的证明。数值例子可以用来反驳它们是否是反例,但不能证明猜想,除非你用详尽的枚举来证明它的正确性,unsigned long long 值的情况是不切实际的。 user2162550 说这很明显,这要归功于二进制补码算法......如果很明显,应该很容易证明:)
  • user2162550 ,证明不是 2 的补码。代码甚至可以使用 1 的补码/符号幅度。
【解决方案3】:

假设 sizeof(unsigned long long) > sizeof(unsigned long) 可以安全地声明为:unsigned long long distance(long x, long y)

但是因为现在补码或其他奇特的格式数字不常用(实际上几乎没有人有机会在这样的机器上编写 C 代码)unsigned long 类型可以适应所有可能的距离。

【讨论】:

  • 不幸的是,该假设不适用于相对常见的平台coliru.stacked-crooked.com/a/f169edbad5839278
  • @ÖöTiib 在该系统上没有必要,因为我什至不知道不是 2 补码的单个。那么就不需要更大的尺寸了。另一方面,我还没有听说过 sizeof(unsigned long long) == sizeof(unsigned long) 的补码系统。所以你的评论意味着你不明白我的回答
  • 我没有投反对票,所以显然其他人也不理解你的答案。请编辑它。
  • 对正确性的关注不是 2 的补码与非 2 的补码编码问题。如果unsigned long 范围至少是long 范围的2 倍,这是非常 常见的,尽管对于合规的实现来说不是必需的。使用此处建议的替代方案,为避免填充和符号问题,此处所需的假设不是尺寸一,而是范围一:#if Alternative_result_type_MAX/2 &gt;= LONG_MAX
猜你喜欢
  • 2014-05-24
  • 1970-01-01
  • 2014-12-07
  • 2012-05-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多