如何根据int的符号进行左移或右移?

问题描述 投票:0回答:2

我需要按

x
位移动
b
,其中
b
可以为正(左移)、零(nop)或负(右移)。

C 位移位不处理负移位

我可以定义一个内联函数或宏来执行此操作吗?理想情况下,当我编写此内容时,我不需要知道

x
的类型 - 宏(或内联函数?)应该支持任何整数类型。

c bit-manipulation bit-shift
2个回答
0
投票

您可以使用三元运算符通过宏来完成此操作,然后它应该适用于任何整数类型。

#define SHIFT(val, amt) ((amt) >= 0 ? (val) << (amt) : (val) >> (amt))

唯一的问题是如果

amt
有副作用,它们会被执行两次。


0
投票

如上面评论12中所述:

您可以使用内联函数来避免对参数进行多次求值:

static inline uint32_t SHIFT(uint32_t val, int16_t amt)
{
    return (amt >= 0) ? val << amt : val >> -amt;
}

选择适合您需求的参数类型 - 但偏移量可能小至

int8_t
,并且在可预见的将来不会遇到范围问题。

请注意,转换有符号类型会产生影响。 C11 标准§6.5.7 移位运算符 设置了规则:

对每个操作数执行整数提升。结果的类型是提升后的左操作数的类型。如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义。

E1 << E2
的结果是
E1
左移
E2
位位置;空出的位用零填充。如果
E1
具有无符号类型,则结果的值为 E1 x 2E2,比结果类型中可表示的最大值减少模一。如果
E1
具有有符号类型和非负值,并且 E1 x 2E2 可在结果类型中表示,则这就是结果值;否则,行为是未定义的。

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

S罗伯特·詹姆斯

如所写,您的想法引起了 GCC 关于转换的大量警告。

我回复:

正如我所说,您需要获得正确的类型 - 并且您很可能需要具有不同类型的多个函数作为返回值和移位值:

  • SHIFT32
    如上
    uint32_t
  • SHIFT64
    对于 64 位类型使用
    uint64_t
  • 也许
    SHIFT16
    对于 16 位类型使用
    uint16_t
  • 也许(但可能不是)
    SHIFT8
    对于 8 位类型使用
    uint8_t

如果需要,您可以为带符号类型添加变体,但是(正如我之前所说),转移带符号类型并不是一个特别好的主意,尽管您可以找到大量可以做到这一点的代码。

您可以使用宏来编写这些函数:

#define GENERATE_SHIFT_FUNCTION(name, type) \
    static inline type name(size value, uint16_t amt) \
    { \
        return (amt >= 0) ? val << amt : val >> -amt; \
    }

GENERATE_SHIFT_FUNCTION(SHIFT64, uint64_t)
GENERATE_SHIFT_FUNCTION(SHIFT32, uint32_t)
GENERATE_SHIFT_FUNCTION(SHIFT16, uint16_t)

请注意,宏调用末尾没有分号。如果有人在场,就会在顶层引入一个空声明。

现在您可以像使用其他函数一样使用这些函数,并且函数的参数会被计算一次,从而避免了当使用宏而不是内联函数时对多次计算产生的担忧

© www.soinside.com 2019 - 2024. All rights reserved.