将 2 uint16_t 合并为 1 uint32_t

Combining 2 uint16_t into into 1 uint32_t

我有 2 个 uint16_t,我想合并成 1 个 32 位数:

uint16_t var1 = 255; // 0000 0000 1111 1111
uint16_t var2 = 255; // 0000 0000 1111 1111

uint32_t var3 = (var1 << 16) +  var2;

我希望 var3 为 0000 0000 1111 1111 0000 0000 1111 1111... 所以十进制为 16711935 但我得到 255(0000 0000 0000 0000 0000 0000 1111 1111)。

有什么想法吗?

谢谢

当一个整数类型被移位时,两个操作数首先被提升,并且生成的整数与提升的左操作数具有相同的类型。

编译器将首先尝试将 uint16_t 提升为 int,如果 int 不能保存该值(即该值大于 INT_MAX)它将晋升为 unsigned int。现在,如果您的系统使用 16-bit ints,结果仍将是 16 位,因此只要您的移位值小于 16,则在左移的情况下您的最高有效位将丢失,从这一点开始行为未按标准定义。

在那种系统中,您需要先转换为更宽的整数类型,例如 uint32_tuint64_t,然后左移。为了安全起见,我们始终可以将移位的左操作数转换为预期的 type,这样我们的代码就不会受到编译器设计者做出的实现决策(例如位宽)的影响。

在某种程度上,这取决于平台。在离我最近的系统上,我们得到了预期的结果,可以用一个简短的程序来演示:

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

int main()
{
    uint16_t var1 = 255; // 0000 0000 1111 1111
    uint16_t var2 = 255; // 0000 0000 1111 1111

    uint32_t var3 = (var1 << 16) +  var2;

    printf("%#"PRIx32"\n", var3);
}

输出为 0xff00ff

但是,您的 var1var2 在进行任何算术运算之前会进行 正常整数提升 。如果提升的类型不能保存中间结果,如您所见,部分计算可能会丢失。

您可以通过在算术之前显式加宽 var1 来避免此问题:

    uint32_t var3 = ((uint32_t)var1 << 16) +  var2;

我系统上的等效失败程序是:

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

int main()
{
    uint16_t var1 = 255; // 00ff
    uint16_t var2 = 255; // 00ff

    uint64_t var3 = ((uint64_t)var1 << 32) +  var2;

    printf("%#"PRIx64"\n", var3);
}

如果我不加宽 var1 如图所示,这会生成 0x1fe 而不是 0xff000000ff(因为在这个系统上,<<32 恰好是一个32 位无符号类型的无操作)。