实施逻辑右移的负数在c

问题描述:

有没有一种简单的方法来做c在负数上的逻辑右移,比如我们如何有>>用于算术右移?实施逻辑右移的负数在c

+0

这应该重复不回答这个问题“有没有办法为负数做逻辑右移”。投票重新开放。 – Lundin

+0

问题是,当前唯一能够可靠地执行逻辑右移的方法是在无符号类型上执行,但是当签名类型是2的补码格式时,仅签名为无符号转换才会保留位模式。所以要么你有一个解决方案依赖于实现定义的行为(假定signed int为2的补码)或者使用'intXX_t/uintXX_t'来指定它。 – user3528438

右移负数将在C中调用实现定义的行为。将会发生的情况不是由标准指定的,而是由编译器指定。所以它可以导致算术移位,也可能导致逻辑移位,或者完全是其他的东西(比如旋转,尽管我从来没有听说过)。除非您阅读特定的编译器文档,否则您无法知道或假定哪种方法适用于您的编译器。

但是,无符号数字总是使用逻辑移位。所以,如果你想在一个负数逻辑移位,可移植的,然后转换为无符号移位之前:

int32_t i = -1; 

i = (int32_t) ((uint32_t)i >> n); // guaranteed to give logical shift 

同样,如果你想保证算术移位:

int32_t i = -1; 
bool negative = i < 0; 

if(negative) 
{ 
    i = -i; 
} 

i = (int32_t) ((uint32_t)i >> n); 

if(negative) 
{ 
    i = -i; 
} 
+0

嗯使用'int32_t/uint32_t'可以保证给出逻辑转移,但是,尽管这是常见的,但这些类型是可选的,因此将可移植性限制在一定程度。由于'i = -i;',第二个“保证算术转换”的UB为-2147483648,但可能按需要工作。 – chux

+0

@chux我不会关心不支持stdint.h的编译器。如果我们可以通过编写依赖于stdint.h的代码来提供帮助,那么这些编译器就需要尽快从市场上移除,然后一切顺利。标准所说的无关紧要,程序员应该要求编译器是适合现实世界的有用工具。 – Lundin

+0

@chux具有值INT_MIN并获得转换的变量不是UB,而仅仅是实现定义的,6.3.1.3/3:“否则,新类型是有符号的,并且该值不能在其中表示;结果是实现定义的或者实现定义的信号被引发。“ – Lundin

逻辑右移位移之前由int值铸造unsigned完成:

int lsr(int n, int shift) { 
    return (int)((unsigned)n >> shift); 
} 

算术右移位不能与直接写给dpne运算符在C中的负值,因为它的效果是实现定义的。这里是没有测试,复制符号位的所有值在二进制补架构的简单替代:

int asr(int n, int shift) { 
    unsigned u = (unsigned)n; 
    return (int)((u >> shift) | ~(((u & (unsigned)INT_MIN) >> shift) - 1)); 
} 

,这里是一个简单的测试程序:

#include <limits.h> 
#include <stdio.h> 
#include <stdlib.h> 

int lsr(int n, int shift) { 
    return (int)((unsigned)n >> shift); 
} 

int asr(int n, int shift) { 
    unsigned u = (unsigned)n; 
    return (int)((u >> shift) | ~(((u & (unsigned)INT_MIN) >> shift) - 1)); 
} 

int main(int argc, char *argv[]) { 
    int n = (argc < 2) ? -2 : strtol(argv[1], NULL, 0); 
    int shift = (argc < 3) ? 2 : strtol(argv[2], NULL, 0); 

    printf("%d >> %d = %d\n", n, shift, asr(n, shift)); 
    printf("%d >>> %d = %d\n", n, shift, lsr(n, shift)); 

    return 0; 
}