C 转换可变位大小的有符号整数

C 转换可变位大小的有符号整数,c,embedded,c99,twos-complement,C,Embedded,C99,Twos Complement,我在一个无符号整数(uint32_t)中有一个位数(位数可以改变)。例如(示例中为12位): 这些位表示该长度的有符号整数。 在这种情况下,十进制数字应为-100。 我想将变量存储在有符号变量中,得到的是实际值。 如果我只是使用: int32_t b = (int32_t)a; 它将只是值3996,因为它被强制转换为(0x00000F9C),但实际上需要是(0xFFFF9C) 我知道一种方法: union test { signed temp :12; }; union test x

我在一个无符号整数(uint32_t)中有一个位数(位数可以改变)。例如(示例中为12位):

这些位表示该长度的有符号整数。 在这种情况下,十进制数字应为-100。 我想将变量存储在有符号变量中,得到的是实际值。 如果我只是使用:

int32_t b = (int32_t)a;
它将只是值3996,因为它被强制转换为(0x00000F9C),但实际上需要是(0xFFFF9C)

我知道一种方法:

union test
{
    signed temp :12;
}; 
union test x;
x.temp = a;
int32_t result = (int32_t) x.temp;
现在我得到了正确的值-100

但是有没有更好的方法呢? 我的解决方案不是很灵活,正如我提到的,位数可以变化(在1-64位之间)。这取决于右移有符号负整数时符号扩展的实现定义行为。首先将无符号整数一直向左移位,直到符号位变为MSB,然后将其强制转换为有符号整数并向后移位:

#include <stdio.h>
#include <stdint.h>

#define NUMBER_OF_BITS 12

int main(void) {
    uint32_t x = 0xF9C;
    int32_t y = (int32_t)(x << (32-NUMBER_OF_BITS)) >> (32-NUMBER_OF_BITS);

    printf("%d\n", y);

    return 0;
}
#包括
#包括
#定义\u位的数量\u 12
内部主(空){
uint32_t x=0xF9C;
int32_t y=(int32_t)(x>(32个位);
printf(“%d\n”,y);
返回0;
}
但是有没有更好的方法呢

这取决于你所说的“更好”的意思。下面的例子显示了一种更灵活的方法,因为位字段的大小没有固定。如果你的用例需要不同的位大小,你可以认为它是一种“更好”的方式。

这是您问题的解决方案:

int32_t sign_extend(uint32_t x, uint32_t bit_size)
{
    // The expression (0xffffffff << bit_size) will fill the upper bits to sign extend the number.
    // The expression (-(x >> (bit_size-1))) is a mask that will zero the previous expression in case the number was positive (to avoid having an if statemet).
    return (0xffffffff << bit_size) & (-(x >> (bit_size-1))) | x;
}
int main()
{

    printf("%d\n", sign_extend(0xf9c, 12)); // -100
    printf("%d\n", sign_extend(0x7ff, 12)); // 2047

    return 0;
}
int32符号扩展(uint32位x,uint32位大小)
{
//表达式(0xFFFFFF>(bit_size-1))是一个掩码,在数字为正数的情况下,该掩码将使前一个表达式归零(以避免出现if状态)。
返回(0xFFFFFF>(位大小-1))| x;
}
int main()
{
printf(“%d\n”,符号扩展(0xf9c,12));/-100
printf(“%d\n”,符号扩展(0x7ff,12));//2047
返回0;
}

签名扩展位字段的无分支方式(Henry S.Warren Jr.,CACM v20 n6,1977年6月)如下:

//位长度len的值i是要签名扩展的位字段
//i是右对齐的,左侧填充零

sext=1明智、可移植且有效的方法就是屏蔽数据部分,然后用0xFF填充所有其他内容。。。为了得到合适的2的补码表示。你需要知道有多少位是数据部分



  • 我们可以用
    (1u到底有多少位?你真的需要知道有多少位,这样你才能知道这个例子中的符号。12位。但是它可以改变。我总是知道有符号变量有多少位。我正在实现一个通信协议。每个变量的长度都是定义的。所以我知道这个变量有12位,但是其他变量可以有一个差异不同的长度。所以我可以使用我提到的解决方案,但是我需要一个从1到64的所有大小的并集。检查有符号位是否根据位的数量设置(根据位的数量移动掩码)然后反转该掩码以选择a的值。然后将该值强制转换为b。并根据先前检索的内容设置b的有符号位。无跳转/测试:
    无符号符号扩展(无符号x,无符号数字位){定义的unsigned high=1实现意味着它不能与所有编译器一起工作?是的。符号扩展是C标准留给编译器实现的选项之一。但您还应该注意,负数表示也是这样一种选择,因此
    0xffff9c
    不会与每个编译器一起使用
    -100
    @EugeneSh。固定长度类型,如
    int32_t
    ,保证是2的补码。但是,不能保证它们会存在,但如果它们存在,它们将是2的补码。@Christiangibons你是对的,谢谢你指出。我总是忘记这一点。你不必担心stdint.h类型是否可用,只需要编写代码这依赖于实现定义的行为是很糟糕的。根据我的经验,正确的转换尤其是不可移植的,大约50%/50%在实现之间的算术或逻辑转换。我不知道Henry S.Warren Jr是谁,但这不起作用,并且给出了错误的结果…Henry S.Warren Jr背后的逻辑是什么Hacker's Delight的作者,除其他外。这很好用,我已经用过很多次了。也许我应该指出,
    I
    sext
    必须有签名类型。
    1是的,这段代码确实与您第一次发布的代码完全不同。但是,如果len是31或32,这段代码也会调用未定义的行为可能期望是32位无符号数字的有效输入。此外,它不可读。它运行速度也不必要地慢,例如,将其与我刚刚突然编写的代码进行比较:。似乎您错过了我对@4386427答案的评论:).@Lundin这两个例程做不同的事情,为什么要比较它们呢?这个代码没有符号扩展,它盲目地使输入为负数。而且在oring之前,
    ~data\u mask
    是无用的。@Nipo为了让整个问题有意义,你必须知道0xF9C是有符号2的补码。你不知道某个随机数是否已签名。@Nipo若要向您详细说明,您不能对已签名类型变量中不存在且设置为负数的数字进行签名扩展。您不能随机获取二进制数据块并“签名扩展”它。将数据转换为有符号整数的符号位会调用未定义的行为,并且程序可以随意崩溃和烧录。您似乎忘记了有符号的数字实际上可能是正数。如果我们将0x2a从7位扩展到32位,我实际上希望得到0x0000002a,而不是0xFFFFA。我们将从您的代码中得到后者。@Lundin,search Wikipe“符号扩展”的直径。第二段是:例如,如果使用六位表示数字“00 1010”(十进制正10),并且符号扩展操作将字长增加到16位,那么新的表示形式就是“0000”
    unsigned sign_extend(unsigned x, unsigned num_bits)
    {
        unsigned f = ~((1 << (num_bits-1)) - 1);
        if (x & f)  x = x | f;
        return x;
    }
    
    
    int main(void)
    {
        int x = sign_extend(0xf9c, 12);
        printf("%d\n", x);
    
        int y = sign_extend(0x79c, 12);
        printf("%d\n", y);
    }
    
    -100
    1948
    
    int32_t sign_extend(uint32_t x, uint32_t bit_size)
    {
        // The expression (0xffffffff << bit_size) will fill the upper bits to sign extend the number.
        // The expression (-(x >> (bit_size-1))) is a mask that will zero the previous expression in case the number was positive (to avoid having an if statemet).
        return (0xffffffff << bit_size) & (-(x >> (bit_size-1))) | x;
    }
    int main()
    {
    
        printf("%d\n", sign_extend(0xf9c, 12)); // -100
        printf("%d\n", sign_extend(0x7ff, 12)); // 2047
    
        return 0;
    }
    
    // value i of bit-length len is a bitfield to sign extend
    // i is right aligned and zero-filled to the left
    sext = 1 << (len - 1);
    i = (i ^ sext) - sext;
    
    #include <stdio.h>
    #include <stdint.h>
    
    int32_t sign_extend (uint32_t x, int32_t len)
    {
        int32_t i = (x & ((1u << len) - 1)); // or just x if you know there are no extraneous bits
        int32_t sext = 1 << (len - 1);
        return (i ^ sext) - sext;
    }
    
    int main(void)
    {
        printf("%d\n", sign_extend(0xF9C, 12));
        return 0;
    }
    
    #include <stdio.h>
    #include <inttypes.h>
    
    int main(void) 
    {
      const uint32_t data_length = 8;
      const uint32_t data_mask = (1u << data_length) - 1;
    
      uint32_t a = 0xF9C;
      a = (a & data_mask) | ~data_mask;
    
      printf("%"PRIX32 "\t%"PRIi32, a, (int32_t)a);
    }
    
    FFFFFF9C        -100