在C中将符号扩展名从16位扩展到32位

Sor*_*ban 6 c binary integer sign

我必须为16位整数做一个符号扩展,由于某种原因,它似乎没有正常工作.谁能告诉我代码中的bug在哪里?我已经工作了几个小时.

int signExtension(int instr) {
    int value = (0x0000FFFF & instr);
    int mask = 0x00008000;
    int sign = (mask & instr) >> 15;
    if (sign == 1)
        value += 0xFFFF0000;
    return value;
}
Run Code Online (Sandbox Code Playgroud)

指令(instr)是32位,在里面我有一个16位的数字.

Naw*_*waz 17

为什么错:

int16_t s = -890;
int32_t i = s;  //this does the job, doesn't it?
Run Code Online (Sandbox Code Playgroud)

  • @ qbert220:你的意思是`int16_t`不一定是16位有符号整数? (4认同)
  • 鉴于问题明确要求从16位扩展到32位,考虑使用`int16_t`和`int32_t`而不是`short`和`int` (3认同)
  • @Nawaz:我认为他正在评论你的原始答案. (3认同)

CAF*_*FxX 12

使用内置类型有什么问题?

int32_t signExtension(int32_t instr) {
    int16_t value = (int16_t)instr;
    return (int32_t)value;
}
Run Code Online (Sandbox Code Playgroud)

或更好(如果通过,这可能会产生警告int32_t)

int32_t signExtension(int16_t instr) {
    return (int32_t)instr;
}
Run Code Online (Sandbox Code Playgroud)

或者,就所有重要事项而言,替换signExtension(value)((int32_t)(int16_t)value)

你显然需要包括<stdint.h>用于int16_tint32_t数据类型.

  • @DipSwitch:我的编译器比你的好,它可以内联这样的函数调用,所以宏没有更快:-) (3认同)

Sur*_*bat 8

刚碰到这个寻找别的东西,也许有点晚了,但也许它对其他人有用.AFAIAC所有C程序员都应该开始编程汇编程序.

无论如何,标志延伸比提案更容易.只需确保使用签名变量,然后使用2班制.

long value;   // 32 bit storage
value=0xffff; // 16 bit 2's complement -1, value is now 0x0000ffff
value = ((value << 16) >> 16); // value is now 0xffffffff
Run Code Online (Sandbox Code Playgroud)

如果变量是有符号的,那么C编译器将>>转换为算术右移保留符号.此行为与平台无关.

因此,假设值以0x1ff开始,那么我们有,<< 16将SL(左移)值,因此instr现在是0xff80,然后>> 16将ASR值,因此instr现在是0xffff.

如果你真的想玩宏,那么尝试这样的事情(GCC中的语法工作没有在MSVC中尝试过).

#include <stdio.h>

#define INT8 signed char
#define INT16 signed short
#define INT32 signed long
#define INT64 signed long long
#define SIGN_EXTEND(to, from, value) ((INT##to)((INT##to)(((INT##to)value) << (to - from)) >> (to - from)))

int main(int argc, char *argv[], char *envp[])
{
    INT16 value16 = 0x10f;
    INT32 value32 = 0x10f;
    printf("SIGN_EXTEND(8,3,6)=%i\n", SIGN_EXTEND(8,3,6));
    printf("LITERAL         SIGN_EXTEND(16,9,0x10f)=%i\n", SIGN_EXTEND(16,9,0x10f));
    printf("16 BIT VARIABLE SIGN_EXTEND(16,9,0x10f)=%i\n", SIGN_EXTEND(16,9,value16));
    printf("32 BIT VARIABLE SIGN_EXTEND(16,9,0x10f)=%i\n", SIGN_EXTEND(16,9,value32));

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这会产生以下输出:

SIGN_EXTEND(8,3,6)=-2
LITERAL         SIGN_EXTEND(16,9,0x10f)=-241
16 BIT VARIABLE SIGN_EXTEND(16,9,0x10f)=-241
32 BIT VARIABLE SIGN_EXTEND(16,9,0x10f)=-241
Run Code Online (Sandbox Code Playgroud)

  • 这会遇到这样的问题:转换到符号位是签名整数的未定义行为:http://blog.regehr.org/archives/738 (2认同)

qbe*_*220 6

尝试:

int signExtension(int instr) {
    int value = (0x0000FFFF & instr);
    int mask = 0x00008000;
    if (mask & instr) {
        value += 0xFFFF0000;
    }
    return value;
}
Run Code Online (Sandbox Code Playgroud)

  • 不要将“ +”用于位操作,此处的正确运算符是“ |”。 (2认同)
  • 这个答案调用UB,假设由于有符号溢出,`int`只有32位.如果您要实现自己的符号扩展,则需要使用无符号类型,但最好让编译器执行此操作. (2认同)

Cha*_*Rex 5

人们指出了强制转换和左移,然后是算术右移。另一种不需要分支的方法:

\n\n
(0xffff & n ^ 0x8000) - 0x8000\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果高 16 位已经为零:

\n\n
(n ^ 0x8000) - 0x8000\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

\xe2\x80\xa2 社区 wiki,因为它是来自“聚合魔法算法,签名扩展”的想法

\n