在C/C++中,符号扩展一个数字的最佳方法是什么?

3

我有一个10位的有符号值。实际上,它是一个10位颜色值,我想要将其转换为int

这10位将被解释为在更大的int中的带符号值,其余的位都是零。

我可以考虑以下3种方法:

#include <stdio.h>

void p(int v)
{
    printf("\t%d", v);
}

int main()
{
    for (int i = -2; i < 3; ++i)
    {
        unsigned int u = i & 0x3ff;

        int v;

        // method 1: test the bit
        v = u;
        if (u & 0x200) v = v ^ -0x400;
        p(v);

        // method 2: shift
        v = u<<(sizeof(int)*8-10);
        v >>= (sizeof(int)*8-10);
        p(v);

        // method 3: use sign extend
        v = (signed char)(u >> 2);
        v <<= 2;
        v |= u;
        p(v);


        printf("\n");

    }

    return 0;
}

有没有更好的方法?在这种位操作问题上,通常有更好的方法。

感谢任何意见。

更新

添加两种方法。第一种是由 @cruz-jean 建议的位域(bit fields),第二种是编译位域后由 gcc 提出的建议。

      // method 4: bit fields
        struct { int v:10; } f;
        f.v = u;
        v = f.v;

        // method 5: sign extend short
        v = (signed short)(u << 6);
        v >>= 6;

有趣的是,MSVC将#4编译成#2,而gcc将#4编译成#5。

方法5看起来不错。


2
你是指 C 还是 C++?我怀疑这个问题应该只有一个标签。 - Fred Larson
1
如果这个值是结构体位域的一部分,你可以通过将其声明为有符号位域来简化它 - C/C++会自动进行所有转换。 - mvp
@Fredrik 我的意思是这10个位应该被解释为有符号数,而不是填满整个字。如果不清楚,抱歉。 - jkj yuio
1
@FredLarson,我的测试程序确实无法编译为C语言,但这个想法对两种语言都是有效的。 - jkj yuio
v = (u & 0x200) ? u - 0x400 : u; - user3386109
显示剩余10条评论
2个回答

3
您可以声明一个10位有符号的位域类型,并将其作为int访问其值:
struct signed_10_bit
{
    int val : 10;
};

int some_10_bit_num = 0x3ff;
int extended = signed_10_bit{ some_10_bit_num }.val;

为了与C兼容,我将它改成了以下代码: struct { int v:10; } f; f.v = u; v = f.v; - jkj yuio
MSVC 生成的移位操作与方法2相同,但是gcc会从10位移位到16位并在那里进行符号扩展。 - jkj yuio
1
@jkjyuio 这是一件好事。写出你的意思,相信编译器能够高效地完成它。现在的优化器非常聪明。 - Cruz Jean
谢谢。我加入了你的建议。位域可能是正确的方式。 - jkj yuio

3

再试一个:

v = u | (0 - (u&0x200));

适用于处理器速度较慢的情况。


我喜欢这个!方法#1是由于慢速移位而产生的灵感,但我不喜欢测试。这个更好。一些旧的CPU只能一次移动1个,并且需要一个移动循环,因此这将是最好的方式。不确定在这些上会生成什么位域。 - jkj yuio

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