Путаница в смещении битов

bitshift

Я пытаюсь считать два отдельных сообщения по 3 байта через CAN, сложить их и отправить обратно через CAN на CANBED V1, который использует Leonardo.

Моя проблема в использовании битового сдвига.

Если я попытаюсь сдвинуть 1 на 1..14 бит, я, кажется, пойму,

uint32_t hdbs = 1 << (14) ;
Serial.println(hdbs, BIN);

Этот код выводит на дисплей 100000000000000 при печати двоичного числа или 16384 в десятичном формате.

Но если я попробую сдвинуть бит на 15 или больше

uint32_t hdbs = 1 << (15) ;
Serial.println(hdbs, BIN);

Теперь я в замешательстве, поскольку

11111111111111111110000000000000000 или 4294934528 в десятичной системе

Какую базовую концепцию я упускаю? Я пробовал использовать unsigned long, long и т. д.

Если я произведу простые вычисления и использую hdbs = 8 * 4095, то получу правильный ответ 32760, но когда я попробую 8*4096, то получу 4294934528, что должно быть 32768 (что, похоже, превышает 16-битную беззнаковую переменную, но я думал, что объявляю hdbs как 32-битную переменную.

, 👍1


2 ответа


1

Это на самом деле больше вопрос по C++, но я не знаю, известно ли вам это. Грубо говоря, в C, C++ (и языке "Arduino") выражения вычисляются для типов параллельно со своими значениями изнутри наружу, следуя правилам приоритета операторов, как в обычной алгебре.

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

uint32_t hdbs = static_cast<uint32_t>(1) << 15;

uint32_t hdbs = (uint32_t)1 << 15;

uint32_t hdbs = uint32_t(1) << 15;

uint32_t hdbs = uint32_t{1} << 15;

uint32_t hdbs = 1UL << 15;

1 имеет тип int, который для Leonardo является 16-битным типом. Обычно, когда бинарные операторы, такие как + или /, оцениваются для типа результата, обе стороны учитываются в том, что называется обычными арифметическими преобразованиями. Для << он просто смотрит на тип смещаемой вещи и берет его. Таким образом, ваш результат 1 << 15 оценивается в int, который не может хранить это значение в AVR. Тот факт, что вы позже используете это при инициализации uint32_t hdbs, не имеет значения при вычислении самого 1 << 15.

Я бы предположил, что вы получите 0x8000, что при преобразовании в 32-битное значение с расширением знака дало бы 11111111 1111111 10000000 00000000. Но попытка переполнить знаковый целочисленный тип, как этот, не определена. Нельзя ожидать, что надежность приведет к какому-либо определенному поведению, не говоря уже о значении.

Если бы вы использовали 32-битный чип, такой как ARM или ESP32/ESP8266, это бы просто сработало, потому что тип int там 32-битный.

,

0

Вот как работает приведение типа C.

Выражения C по умолчанию имеют знаковый тип. Если тип выражения имеет слишком малую длину в битах, то он может стать также отрицательным из-за переполнения (в старший бит, который также является знаковым битом).

Всякий раз, когда знаковое выражение должно вписаться в большее количество бит, оно сначала расширяется до более длинного знакового значения (что фактически то же самое, что дублирование старшего бита столько раз, сколько необходимо). И только после этого оно будет преобразовано в беззнаковое (что по сути является пустой операцией).

Так что, чтобы преодолеть эту проблему, вам нужно изменить порядок приведения, сделав 1U или 1UL вместо просто 1. Тогда вы никогда не столкнетесь со знаковыми значениями и, следовательно, без расширения знакового бита.

,
Смотрите также: