【问题标题】:unary minus for 0x80000000 (signed and unsigned)0x80000000 的一元减号(有符号和无符号)
【发布时间】:2012-03-17 11:56:13
【问题描述】:

n3337.pdf 草案 5.3.1.8 指出:

一元- 运算符的操作数应具有算术或无范围枚举类型,结果是其操作数的否定。对整数或枚举操作数执行整数提升。无符号量的负数是通过从 2ⁿ 中减去它的值来计算的,其中 n 是提升的操作数中的位数。结果的类型是提升操作数的类型。

在某些情况下就足够了。假设 unsigned int 是 32 位宽,那么 (-(0x80000000u)) == 0x80000000u,不是吗?

不过,我在无符号 0x80000000 上找不到任何关于一元减号的信息。此外,C99 标准草案 n1336.pdf, 6.5.3.3 似乎对此一无所知:

一元 - 运算符的结果是其(提升的)操作数的负数。对操作数执行整数提升,结果具有提升的类型。

UPDATE2:假设 unsigned int 是 32 位宽。所以,问题是:C 中的一元减号(有符号和无符号)和 C++ 中的一元减号(仅有符号)呢?

UPDATE1:运行时行为和编译时行为(即常量折叠)都很有趣。

(相关:Why is abs(0x80000000) == 0x80000000?

【问题讨论】:

  • 你的意思是你在 signed 0x80000000 上找不到任何关于一元减号的信息吗?
  • 一般编译器会简单的生成一个合适的CPU操作码(比如x86 NEG)。标准措辞只是试图正式描述实际发生的情况。
  • @GregHewgill:我认为问题的关键在于 C99 标准没有描述会发生什么。
  • 我不明白你在问什么。一元 - 在大多数平台上,0x80000000 等于 0x80000000,因为 0x100000000 - 0x80000000 = 0x80000000。
  • @sth c99 标准似乎对极端情况一无所知,即: 6.5.3.3.3:一元 - 运算符的结果是其(提升的)操作数的负数。对操作数执行整数提升,结果具有提升的类型。

标签: c++ c standards unary-operator


【解决方案1】:

在 n1336,6.3.1.3 有符号和无符号整数中,第 2 段定义了到无符号整数的转换:

否则,如果新类型是无符号的,则值转换为 反复加或减一大于最大值 可以用新类型表示,直到值在 新类型。

所以对于 32 位无符号整数,-0x80000000u==-0x80000000 + 0x100000000==0x80000000u

【讨论】:

  • 您正在假设常量0x80000000 的类型中可以表示的最大值。
  • @caf,啊,是的。好吧,无论如何你都会得到答案。
【解决方案2】:

对于您的问题,您引用的重要部分是:

一个无符号量的负数是通过减去它的 来自 2ⁿ 的值,其中 n 是提升的操作数中的位数。

所以,要知道-0x80000000u 的值是多少,我们需要知道n0x80000000u 类型中的位数。这至少是 32,但这是我们所知道的(没有关于您的实现中类型大小的更多信息)。给定n 的一些值,我们可以计算出结果是什么:

n   | -0x80000000u 
----+--------------
32  | 0x80000000
33  | 0x180000000
34  | 0x380000000
48  | 0xFFFF80000000
64  | 0xFFFFFFFF80000000

(例如,unsigned int 为 16 位,unsigned long 为 64 位的实现将有 64 个 n)。


C99 在 §6.2.5 类型 p9 中隐藏了相同的措辞:

涉及无符号操作数的计算永远不会溢出,因为 无法用生成的无符号整数表示的结果 type 以比最大数大一的数为模减少 可以由结果类型表示的值。

一元 - 运算符对非零无符号操作数的结果将始终被此规则捕获。

对于 32 位 int0x80000000 的类型将是 unsigned int,不管缺少 u 后缀,所以结果仍然是 0x80000000 类型的值 unsigned int .

如果您改用十进制常量2147483648,它将具有long 类型,并且计算将被签名。结果将是值-2147483648,类型为long

【讨论】:

  • 我认为 int 至少保证 32 位,不是吗?
  • @dauphic:不,int 保证至少为 16 位,long 保证至少为 32 位,long long 保证至少为 64 位。为了四舍五入,char 保证至少为 8 位,short 保证至少为 16 位。
  • 请注意,POSIX 要求 int 至少为 32 位。
  • @caf 当然。假设 unsigned int 是 32 位宽。我仍然对 c++ 中带符号的 int 0x80000000 的一元减号感兴趣。
  • @user1123502:如果 -INT_MIN 不可表示(如二进制补码实现),则根据 C99 §6.5 表达式 p5 未定义行为。在实践中您不会经常看到崩溃,但您经常会看到其他类似的表达式,例如 INT_MIN / -1
猜你喜欢
  • 2011-05-30
  • 2012-03-17
  • 2015-09-14
  • 1970-01-01
  • 2019-03-16
  • 1970-01-01
  • 1970-01-01
  • 2019-01-15
  • 2016-01-02
相关资源
最近更新 更多