是否有任何2补码平台/编译器,其中有符号移位不能执行通常的操作?

时间:2018-08-17 09:55:52

标签: c++ undefined-behavior

左移的结果可以为undefined behavior

  

E1 << E2的值是E1左移E2位的位置;空出   位为零。如果E1具有无符号类型,则   结果是E1×2 ^ E2,比最大值模减少了1   在结果类型中可以表示。否则,如果E1具有带符号的类型   和非负值,并且E1×2 ^ E2在   结果类型对应的无符号类型,然后是该值,   转换为结果类型,就是结果值;否则,   行为是不确定的。

右移的结果可以为implementation-defined

  

E1 >> E2的值是E1右移E2位的位置。如果E1有   无符号类型,或者如果E1具有带符号类型和非负值,   结果的值是   E1 / 2 ^ E2。如果E1具有带符号的类型和负值,则结果为   值是实现定义的。

现在,我所知道的所有平台,未定义的行为/实现定义实际上在这里做了明智的事情:

  • 左移负数等于2 ^ E2,就像该数为正数一样
  • 右移负数是“算术移位”,它通常将数字移位,但是将符号位放入最高有效位。

因此,问题是,是否有任何 2-complement 平台/编译器,它们的行为不像这样?


为什么要这样?大多数编译器don't emit optimal code(由phuclv提供的链接,请检查test1test2之间的反汇编),以实现certain circumstances中的2幂除法(尽管clang生成了最佳代码) )。

1 个答案:

答案 0 :(得分:1)

某些用于处理器的编译器缺少符号扩展的右移指令,过去一直将所有右移运算都视为零填充。

C89标准精确定义了在所有不使用有符号或无符号类型的填充位的全1或2补码平台上左移负数的行为。然而,补码机上的行为并不等同于乘法。此外,尚不清楚在符号幅度机器上的正确行为。

C99标准将负向左移更改为“不确定行为”。基本原理中的任何内容都没有提供更改的任何理由,甚至也没有承认它。基本原理中未提及,表明它没有被视为重大变化,因为没有理由期望任何在C89下有效定义行为的实现都不会继续以相同方式定义行为。该标准继续要求它。唯一有意义的意图是允许人们的补码或符号幅度实现(如果为C99生产了任何形式的实现),其行为可能比C89所要求的有用。

在编写C89甚至C99时,作者并没有意识到标准所要求的行为与标准可以要求它们以可预测且有用的方式处理的行为之间的区别。不。编译器作者似乎认为委员会打算从语言中消除后一种形式的所有动作,但是我看不到有任何证据可以暗示这种形式。

实际上,我所知道的唯一编译器目前不遵守关于左移的期望,其结果可以表示为不会溢出的乘法,是那些明确配置为在负向左left缩的编译器-移位纯粹是因为标准不再定义其行为。但是,如果某个“聪明”的人基于标准不再要求实现此类平台以使其在平台中正常运行的事实而使“补码”编译器“最佳化”,我不会感到特别惊讶。他们总是有同样明智而有用的方式。这种偏离可以采取两种形式:

  1. 编译器可以确定,如果要求进位标志清零的操作之前是带符号的左移,则可以省略通常在后者操作之前的“清除进位”指令。我已经在可以保存指令的平台上使用了,但是我见过的针对此类平台的所有编译器都清除了进位,即使标准不需要它。

  2. 编译器可以确定左移结果为负是“不可能的”,因此可以安全地省略该结果与任何负值之间的任何比较。再进一步,它可以确定操作数同样不可能为负数,并删除所有带有负数的比较,这些比较不会阻止左移。 gcc和clang都没有尝试进行这种优化,但这并不意味着它们永远也不会。

二进制补码系统的质量实现将可预测地处理带符号的左移,除非或直到有特别令人信服的理由使它们执行否则,这似乎不太可能发生。至于质量差的实现可能会做什么,谁知道。