有符号整数的位运算结果是否被定义?

72

我知道对有符号整数进行位右移运算 >> 的行为可能依赖于具体的实现(特别是当左操作数为负数时)。

那其他运算符呢:~>>&^|?如果它们的操作数是内置类型的有符号整数(shortintlonglong long),结果是否保证与其无符号类型在比特内容上相同?


3
关于 ~、&、^、| 这些符号并不难理解,因为它们都是在二进制位级别上操作。有符号/无符号并不重要。如果是有符号数,>> 通常被实现为算术右移(正确地除以 2 个数字),如果是无符号数,>> 就是逻辑右移(用 0 填充)。<< 也没有什么混淆的地方,因为我们只需要将左边的位移出去(并在右边填充 0)。 - nhahtdh
http://en.wikipedia.org/wiki/Signed_number_representations - TemplateRex
1
你指的"其他"操作符中是不是应该用<< 而不是 >> - Toby Speight
1
移除C++标签 - C和C++是不同的语言,除非问题涉及互操作性或其他相关问题,否则问题不应该同时打上两个标签;现有的答案都只涉及到了C语言。 - M.M
4个回答

61

对于负操作数,<< 的行为未定义,>> 的结果是实现定义的(通常作为“算术”右移)。<<>> 概念上不是按位运算符。它们是算术运算符,对于其上定义良好的操作数等效于乘以或除以相应的二次幂。

至于真正按位运算符^~|&,它们在操作数的(可能被提升的)类型的位表示中起作用。它们的结果对于每种可能的有符号表示(二进制补码、反码或原码)都是定义良好的,但在后两种情况下,如果实现将“负零”表示视为陷阱,则可能会导致结果成为陷阱表示。个人而言,我几乎总是使用无符号表达式与按位运算符一起使用,这样结果在值方面是100%定义良好的,而不仅仅是在表示方面。

最后,请注意,本答案所述内容可能仅适用于C。C和C++是非常不同的语言,虽然我不熟悉C ++,但我了解它在一些这些方面可能与C不同...


9
在二进制补码的情况下,~INT_MAX 也可以成为陷阱表示。 - caf
5
顺便提一句,根据第6.2.6.1条规定,如果负零存在,则在位运算操作数中需要与普通零的行为完全相同:如果一个运算符应用于一个具有多个对象表示的值,则使用哪个对象表示不应影响结果的值。 - R.. GitHub STOP HELPING ICE
5
这是一个有趣的观点 - 它似乎暗示着在支持负零的补码实现中 ~~INT_MAX == INT_MIN - caf
5
C语言允许使用2进制补码的变体,其中INT_MIN==-INT_MAX。:-( - R.. GitHub STOP HELPING ICE
7
请参见6.2.6.2整数类型。在第2段的结尾处: 其中应用哪一个是实现定义的,对于带有符号位为1和所有值位为零的值(对于前两个)或具有符号位和所有值位为1(对于反码),是否为陷阱表示或正常值也是实现定义的 - R.. GitHub STOP HELPING ICE
显示剩余5条评论

14
  • 负数进行左移操作 <<,其行为未定义;
  • 负数进行右移操作 >>,会得到一个实现定义的结果;
  • &|^ 运算符的结果是根据数值的位表示来定义的。在 C 语言中,负数有三种可能的表示形式:二进制补码、反码和原码。当对负数使用这些运算符时,所使用的实现方法将决定数值结果。

请注意,对于二进制补码和原码,带有符号位为 1 且所有数值位都为零的值,或者带有符号位和所有数值位都为 1(对于反码)的值被明确允许作为陷阱表示法。 在这种情况下,如果您使用这些运算符的参数会生成此类值,则其行为未定义。


抱歉如果我误解了你的写法。当你说“...带有符号位1和所有值位为零(对于二进制补码...)的值被明确允许成为陷阱表示”,听起来好像你在说这是一个无效的值。但在二进制补码中,我相信它代表了该大小的最小负值,定义明确。 - Dan
对之前评论的跟进(编辑):根据你上面的评论,我第一次知道最大正有符号整数的按位补码可能是一个陷阱值。之前没有意识到这一点,或者6.2.6.2。 - Dan

4

二进制内容不会改变,但结果值仍然取决于具体实现。

在使用位运算时,你真的不应该将值视为有符号或无符号,因为那是在不同的层次上操作。

使用无符号类型可以避免一些麻烦。


2
C89标准基于位位置定义了左移有符号数的行为。如果没有填充位,无论是有符号类型还是无符号类型,所需的无符号类型行为与正有符号类型共享相同的表示要求暗示符号位紧挨着最高有效位。
在C89中,-1<<1在没有填充位的二进制补码实现中将是-2,在没有填充位的一进制补码实现中将是-3。如果有任何没有填充位的符号大小实现,则在这些实现中-1<<1将等于2。
C99标准将负值的左移更改为未定义行为,但是理由中没有任何线索说明为什么(甚至没有提到更改)。 C89所需的行为可能在某些一进制补码实现中不太理想,因此允许这些实现自由选择更好的内容是有意义的。我没有看到任何证据表明标准的作者并不希望优质的二进制补码实现继续提供C89规定的相同行为,但不幸的是他们实际上并没有这样说。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接