Несколько вопросов по битшифтингам!
Итак, int
в Arduino составляет 2 байта, что технически может содержать значение 65 535. Однако старший бит используется как знаковый бит, так что теперь у нас есть от -32 768 до 32 767. Итак, это целое число со знаком, простая вещь. Пока я возился с битшифтингом, я наткнулся на кое-что интересное. Если я сделаю следующий код:
int a = 1023 << 6; // 1023 = 0b0000001111111111
Serial.println(a, BIN);
он возвращает нелепое число 11111111111111111111111111000000, что составляет 4 байта. Я знаю, что это какая-то ошибка со сдвигом бита в бит со знаком, потому что если я это сделаю
int a = 1023 << 5; // равно 111111111100000, как и ожидалось
Все работает отлично. Вопрос 1. Что здесь происходит? Откуда взялось это число?
Поэтому я поумнел (так я думал) и понял, что если мне нужно больше байтов, я просто использую long
. Поэтому я попробовал следующий код:
long a = 1023 << 6;
Serial.println(a, BIN);
Я рассчитывал, что у меня будет 3 "дополнительных" байтов, чтобы переместить мой номер в. Однако возвращается тот же номер. Вопрос 2: Что происходит?
Вопрос 3. Каков порядок операций при битовом сдвиге?
Wire.write(CMD_VDR | PD_NPD | voltageLevel >> 6);
Этот код, который я написал, дает правильный результат. Однако каков порядок действий? Это просто слева направо? Помещение voltageLevel >> 6
в скобках не меняет вывод. Он работает нормально, но я хочу знать почему!
Спасибо!
@HavocRC, 👍-1
Обсуждение2 ответа
Лучший ответ:
timemage предоставил отличный ответ на ваш первый вопрос (проголосовал за!). Попробую ответить на следующие:
long a = 1023 << 6;
Здесь вы оцениваете 1023 << 6
, затем присваивая результат
длинный
. Важно понимать, что правила C++ для вычисления
выражение не зависит от того, что вы собираетесь делать позже с
результат. Учитывая, что 1023
является константой int
, выражение имеет вид
введите int
и оценивается как таковой. Результат тогда не определен
поведение, которое просто происходит, потому что вам не повезло, дать
ожидаемое значение, то есть −64.
Присвоение значения -64 long
приводит к преобразованию типа с сохранением значения.
Это расширение подписи в действии. Как пояснил timemage,
println(long, int)
приводит значение к unsigned long
, поэтому
результат вы видите.
Какой порядок операций
[в выраженииCMD_VDR | ПД_НПД | Уровень напряжения >> 6
]?
Сдвиг имеет более высокий приоритет, чем побитовое ИЛИ. Выражение таким образом эквивалентно
CMD_VDR | PD_NPD | (voltageLevel >> 6)
См. Приоритет оператора C++.
Спасибо, что поделились списком приоритетов! Это очень полезно., @HavocRC
Ну, технически я попытался ответить на второй. Просто это было не так ясно, как ты выразился. Память, о которой я говорил, была промежуточным результатом правой части, которая применима к *обоим* их выражениям. Но, поскольку вы написали это, я просто проголосую за вас, а не попытаюсь прояснить, что я имел в виду., @timemage
Итак,
int
в Arduino составляет 2 байта
В общем случае это неверно, как бы там ни было. Это верно для Arduino на основе AVR, которые, как я предполагаю, используются ниже.
Строго говоря, когда вы делаете int a = 1023 << 6;
с 16-битным целым числом все ставки сняты, потому что вы уже находитесь в неопределенном поведении. 1023 — это тип int
, правая часть должна иметь тип int
, и попытка сохранить что-то, что на самом деле не помещается в целое число со знаком, не определена.
Но на данный момент, давайте просто скажем, что он делает примерно то, что вы ожидаете от битового шаблона, как и для беззнакового типа. Вы создали битовое представление для 16-битного целого числа со знаком со значением -64.
Вы вызываете println(int, int)
здесь.
Он, в свою очередь, вызывает println(long, int)
здесь. Он обрабатывает только signed
ness для основания 10. Итак, вы просто перенаправляете свое значение -64 в printNumber
здесь, которые обрабатывают значения unsigned long
(которые могут иметь префикс -
для базы 10).
Итак, вы конвертируете -64 в unsigned long
. Преобразование в типы без знака уменьшает их максимальное значение по модулю плюс 1. Максимальное значение 32-битного unsigned long
равно 0xFFFFFFFF, с добавлением единицы вы получите 0x100000000. Другими словами, вы в конечном итоге печатаете 0x100000000 - 64, то есть число, которое вы видите.
1023 << 5
не переполняет 16-битное целое число со знаком. Не приводит к отрицательному числу (опять же, если мы снисходительно относимся к неопределенному поведению), поэтому вы видите другой результат.
«Однако это верно для Arduino на основе AVR», — (не призывая вас к этому, но) для полноты стоит упомянуть, что int
- это 2 байта на AVR Arduino _с компилятором gcc_, потому что это то, что разработчики того, что компилятор выбрал для этого. Другой компилятор C/C++ на том же оборудовании может работать по-другому. Другими словами, ширина int
зависит не от аппаратного обеспечения, а от компилятора. Ширина int
обычно выбирается как собственный размер слова этого оборудования, но на AVR это будет 8 бит, что не так полезно, как 16., @JRobert
@JRobert Да, я не знал, как далеко они хотели зайти. На самом деле было бы несовместимо иметь 8-битный тип int
, который не соответствует минимальному требуемому диапазону, хотя gcc позволит вам сделать это с помощью -mint8
. Но да, совместимый компилятор может ориентироваться на AVR и делать множество подобных вещей. Например, вы можете сделать все целочисленные типы 64-битными, даже тип char
, и задать CHAR_BIT
равным 64. Опять же, трудно понять, на чем остановиться., @timemage
Я мог бы написать что-то вроде *"Действительный диапазон типа int
определяется компилятором, а не аппаратным обеспечением. Он подчиняется требованиям стандартов в той мере, в какой компилятор пытается придерживаться стандарта, который частично находится под контроль над тем, какие параметры были указаны для компилятора. Обычно намерение состоит в том, чтобы он следовал стандарту и, где это возможно, следовал некоторому понятию естественного целочисленного размера базовой архитектуры. Подробности, в конечном счете, именно компилятор определяет размер типа int
."*, @timemage
Спасибо за подробный ответ, @HavocRC
@timemage: Достаточно честно! Я склонен видеть детали...., @JRobert
- C++ против языка Arduino?
- Как использовать SPI на Arduino?
- Какие накладные расходы и другие соображения существуют при использовании структуры по сравнению с классом?
- Ошибка: expected unqualified-id before 'if'
- Что лучше использовать: #define или const int для констант?
- Функции со строковыми параметрами
- Библиотека DHT.h не импортируется
- ошибка: ожидаемое первичное выражение перед токеном ','
Ваша третья часть была добавлена после того, как я начал отвечать, и она в значительной степени не связана с остальной частью. Вероятно, вы можете разобраться, посмотрев [это](https://en.cppreference.com/w/cpp/language/operator_precedence)., @timemage
исследование
расширение знака
, @jsotolaПожалуйста, не размещайте несколько несвязанных вопросов в одном сообщении., @the busybee