Объединение 2 uint16_t в 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).

Есть идеи?

Спасибо

2 ответа

Решение

В некоторой степени это зависит от платформы. На моей ближайшей системе мы получаем ожидаемые результаты, которые можно продемонстрировать с помощью короткой программы:

#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,

Тем не менее, ваш var1 а также var2 проходите нормальные целочисленные продвижения перед любой арифметикой. Если продвинутые типы не могут содержать промежуточный результат, часть вычисления может быть потеряна, как вы видите.

Вы можете избежать проблемы, явно расширив 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);
}

Это производит 0x1fe вместо 0xff000000ff если я не расширю var1 с приведением, как показано (потому что в этой системе, <<32 бывает неоперативным с 32-битными беззнаковыми типами).

Когда целочисленный тип смещается, оба операнда повышаются первыми, и получающееся целое число имеет тот же тип повышенного левого операнда.

Компилятор сначала попытается продвинуть uint16_t в int и если int не может содержать значение (т. е. значение больше, чем INT_MAX), к которому оно будет добавлено unsigned int, Теперь, если ваша система использует 16-bit ints результат по-прежнему будет 16 бит, и, следовательно, ваши самые значимые биты в случае сдвига влево будут потеряны до тех пор, пока значение вашего сдвига меньше 16, с этого момента поведение не определено стандартом.

В такой системе вам нужно сначала привести более широкий целочисленный тип, такой как uint32_t или же uint64_t а затем левую смену. И чтобы быть в безопасности, мы всегда можем привести левый операнд сдвига к ожидаемому type так что на наш код не влияют решения по реализации, принятые конструктором компилятора, такие как битовая ширина.

Другие вопросы по тегам