是否有任何C99编译器的默认设置为-1>> 1!= -1?

时间:2013-11-20 17:46:26

标签: c++ c c99 bit-shift c11

许多人经常在对右移运算符的讨论中指出C标准明确指出右移负数的影响是实现定义的。鉴于C编译器已被用于为不使用二进制补码算法的各种平台生成代码,我可以理解该语句的历史基础。然而,我所知道的所有新产品开发都围​​绕着处理器,这些处理器除了二进制补码之外没有固有的任何整数运算支持。

如果代码希望以2的幂为单位执行内部有符号整数除法,并且它只会针对当前或未来的体系结构运行,那么任何未来的编译器都存在任何逼真的危险要把右移算子解释为做其他事情吗?如果存在现实可能性,是否有任何好的方法来提供它而不会对可读性,性能或两者产生负面影响?是否有任何其他依赖关系可以证明对运算符的行为进行彻底的假设(例如,代码在不支持函数X的实现上将是无用的,并且如果它们不使用符号扩展的右移,则实现不太可能支持X. )?

注意:我在C99和C11标签下询问是因为我希望更新的语言功能将成为支持的平台之一,这意味着平台可能会使用一个算术等效于权利的右移。地板部门,并且有兴趣了解任何以其他方式实现右移的C99或C11编译器。

2 个答案:

答案 0 :(得分:3)

这只是其中一个原因之一,但考虑信号处理案例:

1111 0001 >> 1
0000 1111 >> 1

以右移算术(SRA)的形式,您可以参考以下内容:

1111 0001 >> 1 = 1111 1000
OR
-15 >> 1 = -8

0000 1111 >> 1 = 0000 0111
OR
15 >> 1 = 7

那么问题是什么?考虑幅度为15“单位”的数字信号。将此信号除以2 应该产生等效行为而不管符号。 然而,如上所述,SRA的正信号将导致具有7幅度的信号,而15的负信号将导致具有8幅度的信号。这种不均匀性导致输出中的DC偏压。出于这个原因,一些DSP处理器选择实现“舍入到0”右移算术或其他方法。因为C99标准本身就是这样的,所以这些处理器仍然可以兼容。

在这些处理器上,-1 >> 1 == 0

Related Wiki

答案 1 :(得分:1)

理论上,现在编译器实现中存在一些细微之处可能会滥用所谓的“未定义行为”,超出了后端cpu对寄存器(或“文件”或内存位置或其他)上的实际整数所做的事情:< / p>

  1. 交叉编译器通常是这样的:编译器在执行简单计算时可能会滥用依赖于实现的规范。考虑目标体系结构实现这种方式并托管另一种方式的情况。在您的特定示例中,编译时常量可能最终为1,即使目标体系结构中的任何程序集输出都会给出0(我想不出这样的体系结构)。然后再说一遍,反之亦然。对于编译器实现者来说,没有其他要求(除了基于用户的抱怨)。

  2. 考虑CLANG和其他生成中间抽象代码的编译器。没有什么能阻止类型机制在一些代码路径上的中间时间优化某些操作直到最后一位(即当它可以将代码减少到常量时,想到循环折叠),同时让程序集后端在运行时解决这个问题。其他路径。换句话说,您可以看到混合行为。在这种抽象中,除了C语言所期望的任何标准之外,实施者没有义务遵守任何标准。想想所有整数数学都是由任意精度算术库完成而不是直接映射到主机cpu整数的情况。实现可能会因任何原因决定这是未定义的并且会返回0.它可以为任何已签名的算术未定义行为执行此操作,然后在ISO C标准中有很多,特别是包装等。

  3. 考虑(理论)情况,编译器劫持子操作,而不是发出完整指令来执行低级操作。一个例子是具有桶形移位器的ARM:显式指令(即add或其他)可以具有范围和语义,但是子操作可以以稍微不同的限制操作。编译器可以利用它来限制行为可能不同的地方,例如一个案例可以设置结果标志而另一个案例不能。我不能想到一个重要的具体案例,但有些奇怪的指令只能处理“其他正常行为”的子集,并且编译器可能认为这是一个很好的优化,因为未定义的行为应该真的意味着未定义: - )

  4. 除了奇怪的架构,你在运行时实际上会有奇怪的行为,这些是我能想到为什么你不能假设超出未定义行为的原因的一些原因。

    尽管如此,我们还必须考虑:

    1. 您要求使用C99编译器。大多数奇怪的架构(即嵌入式目标)都没有C99编译器。
    2. 大多数“大规模”编译器实现者处理非常大的用户代码库,并且通常通过过度优化微小的细微差别来支持梦魇。所以他们没有。或者他们按照其他玩家的方式来做。
    3. 在有符号整数“未定义行为”的特定情况下,通常互补的无符号运算是一个已定义的运算,即我看到代码转换符号为unsigned只是为了执行op然后再将结果转换回来。
    4. 我认为我能给出的最好的答案是“你可能会认为这一切都无关紧要,但也许你不应该这样做。”