Объединение 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 int
s результат по-прежнему будет 16 бит, и, следовательно, ваши самые значимые биты в случае сдвига влево будут потеряны до тех пор, пока значение вашего сдвига меньше 16, с этого момента поведение не определено стандартом.
В такой системе вам нужно сначала привести более широкий целочисленный тип, такой как uint32_t
или же uint64_t
а затем левую смену. И чтобы быть в безопасности, мы всегда можем привести левый операнд сдвига к ожидаемому type
так что на наш код не влияют решения по реализации, принятые конструктором компилятора, такие как битовая ширина.