【问题标题】:Unsigned vs signed range guarantees无符号与有符号范围保证
【发布时间】:2012-07-07 11:50:32
【问题描述】:

我花了一些时间研究标准参考资料,但我无法找到以下问题的答案:

  • C/C++ 标准是否在技术上保证,给定有符号整数类型 S 及其无符号对应 U,每个可能的 S 的绝对值始终小于或等于 U 的最大值?

我得到的最接近的来自 C99 标准的 6.2.6.2 部分(C++ 的措辞对我来说更神秘,我认为它们在这方面是等价的):

对于有符号整数类型,对象表示的位应分为三个 组:值位、填充位和符号位。 (...) 作为值位的每个位应与对应无符号类型的对象表示中的相同位具有相同的值(如果有符号类型中有 M 个值位,无符号类型中有 N 个值位,则 M≤ N)。

那么,在假设的 4 位有符号/无符号整数类型中,是否有任何东西阻止无符号类型具有 1 个填充位和 3 个值位,而有符号类型具有 3 个值位和 1 个符号位?在这种情况下,无符号的范围是 [0,7],而有符号的范围是 [-8,7](假设二进制补码)。

如果有人好奇,我现在依靠一种技术来提取负整数的绝对值,该技术首先是对无符号对应项的强制转换,然后是一元减号运算符的应用(这样例如 -3 通过强制转换变为 4,然后通过一元减号变为 3)。这会破坏上面的 -8 示例,它不能用无符号类型表示。

编辑:感谢 Keith 和 Potatoswatter 下面的回复。现在,我的最后一点疑问是标准措辞中“子范围”的含义。如果这意味着严格的“小于”包含,那么我上面的示例和 Keith 下面的示例不符合标准。如果子范围可能是无符号的整个范围,那么它们就是。

【问题讨论】:

  • 在 C 中,您可以保证固定大小的类型 intN_t resp。 uintN_t。不幸的是,我们不保证会提供它们。

标签: c++ c


【解决方案1】:

对于 C,答案是否定的,没有这样的保证。

我将讨论类型intunsigned int;这同样适用于任何对应的有符号和无符号类型对(charunsigned char 除外,它们都不能有填充位)。

在您引用的部分中,标准隐含地保证UINT_MAX >= INT_MAX,这意味着每个非负的int 值都可以表示为unsigned int

但以下是完全合法的(我将使用** 来表示求幂):

CHAR_BIT == 8
sizeof (int) == 4
sizeof (unsigned int) == 4
INT_MIN  = -2**31
INT_MAX  = +2**31-1
UINT_MAX = +2**31-1

这意味着int 有 1 个符号位(它必须)和 31 个值位,一个普通的 2 的补码表示,unsigned int 有 31 个值位和一个填充位。 unsigned int 设置了填充位的表示可能是陷阱表示,或者是未设置填充位的值的额外表示。

这可能适用于支持 2 补码有符号算术但对无符号算术支持较差的机器。

鉴于这些特征,-INT_MIN(数学值)超出了unsigned int 的范围。

另一方面,我严重怀疑是否存在这样的现代系统。填充位是标准允许的,但非常罕见,我不希望它们变得更常见。

您可以考虑添加如下内容:

#if -INT_MIN > UINT_MAX
#error "Nope"
#endif

到您的源代码,因此只有当您可以做您想做的事情时它才会编译。 (当然,您应该想出比"Nope" 更好的错误消息。)

【讨论】:

  • -INT_MIN 很可能等于 INT_MIN,即负值 :vP
  • 有一对整数类型有保证,即signed charunsigned char。这些不允许有填充位。因此,SCHAR_MIN 的否定值将始终适合 unsigned char
  • 我认为测试 INT_MAXUINT_MAX 会更简单,这样的测试也可以推广到其他整数类型,而不是依赖于 uintmax_t 算术。
  • @JensGustedt:测试INT_MAX 不区分INT_MIN == -INT_MAXINT_MIN == -INT_MAX-1
  • 或者更好的是,另一种计算绝对值而不是抛出错误的方法! ;)
【解决方案2】:

你明白了。在 C++11 中,措辞更加清晰。 §3.9.1/3:

有符号整数类型的非负值范围是对应无符号整数类型的子范围,每个对应的有符号/无符号类型的值表示应相同。

但是,这两种对应类型之间的联系究竟有什么意义呢?它们的大小相同,但如果您只有局部变量,那就没关系了。

如果有人好奇,我现在依靠一种技术来提取负整数的绝对值,该技术首先是对无符号对应项的强制转换,然后是一元减号运算符的应用(这样例如 -3 通过强制转换变为 4,然后通过一元减号变为 3)。这会破坏上面的 -8 示例,它不能用无符号类型表示。

您需要处理机器支持的任何数字范围。与其强制转换为无符号对应,不如强制转换为任何无符号类型就足够了:如有必要,比对应的大一个。如果没有足够大的类型存在,那么机器可能无法执行您想要的操作。

【讨论】:

  • 当然,只有当你真的计划支持一些奇异的机器时,这一切才有意义。
  • 我不是特别喜欢,但我正在尝试尽可能地便携。我的具体问题是我正在处理一个仅以无符号表示的外部 API,但负值作为输入也是有意义的(它是一个支持无符号整数的加法和减法的类 - 可以使用负整数实现加法作为减法无符号,取绝对值后)。
  • @bluescarni 那么您应该只尝试与该 API 一样可移植。如果它假设一个一对一的映射,那么试图解决所有在一台奇怪的机器上被破坏的案例是没有意义的。
猜你喜欢
  • 2011-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-09
  • 2016-02-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多