Содержание
- 1 Сколько битов в Байте
- 2 Другие системы счисления
- 3 Десятичные дроби
- 4 Байт
- 5 Приставки К, М, Г, Т («кило-», «киби-» и т.д.)
- 6 Кодировки
- 7 “Трюки” с битами
- 8 Целые
- 9 Десятичные дроби
- 10 Строки
- 11 Важные страницы
- 12 Важные страницы
- 13 Двоичная система и хранение данных
- 14 Шестнадцатеричная система счисления
- 15 Битовые операции
Сколько битов в Байте
Как Вы уже поняли выше, сам по себе, бит — это самая маленькая единица в системе измерения информации. Оттого и пользоваться ею совсем неудобно. В итоге, в 1956 году Владимир Бухгольц ввёл ещё одну единицу измерения — Байт, как пучок из 8 бит. Вот наглядный пример байта в двоичной системе:
00000001 10000000 11111111
Таким образом, вот эти 8 бит и есть Байт. Он представляет собой комбинацию из 8 цифр, каждая из которых может быть либо единицей, либо нулем. Всего получается 256 комбинаций. Вот как то так.
Килобайт, Мегабайт, Гигабайт
Со временем, объёмы информации росли, причём в последние годы в геометрической прогрессии. Поэтому, решено было использовать приставки метрической системы СИ: Кило, Мега, Гига, Тера и т.п.
Приставка «кило» означает 1000, приставка «мега» подразумевает миллион, «гига» — миллиард и т.д. При этом нельзя проводить аналогии между обычным килобитом и килобайтом. Дело в том, что килобайт — это отнюдь не тысяча байт, а 2 в 10-й степени, то есть 1024 байт.
Соответственно, мегабайт — это 1024 килобайт или 1048576 байт.
Гигабайт получается равен 1024 мегабайт или 1048576 килобайт или 1073741824 байт.
Для простоты можно использовать такую таблицу:
Для примера хочу привести вот такие цифры:
Стандартный лист А4 с печатным текстом занимает в средем около 100 килобайт
Обычная фотография на простой цифровой фотоаппарат — 5-8 мегабайт
Фотографии, сделанные на профессиональный фотоаппарат — 12-18 мегабайт
Музыкальный трек формата mp3 среднего качества на 5 минут — около 10 мегабайт.
Обычный фильм на 90 минут, сжатый в обычном качестве — 1,5-2 гигабайта
Тот же фильм в HD-качестве — от 20 до 40 гигабайт.
P.S.:
Теперь отвечу на вопросы, которые мне наиболее часто задают новички.
1. Сколько Килобит в Мегабите? Ответ — 1000 килобит (по системе СИ)
2. Сколько Килобайт в Мегабайте? Ответ — 1024 Килобайта
3. Сколько Килобит в Мегабайте? Ответ — 8192 килобита
4. Сколько Килобайт в Гигабайте? Ответ — 1 048 576 Килобайт.
Другие системы счисления
В цифровой технике также применяется восьмеричная система счисления, но она не нашла применения в микроконтроллерах.
Теоретические можно получить бесконечное значение систем счисления: троичную, пятиричную и даже сторичную, т.е. с любым основанием. Однако практической необходимости в этом пока что нет.
Наиболее простой и быстрый способ преобразования чисел с одной системы счисления в другую – это применение встроенного в операционную систему калькулятора. Найти его можно следующим образом: Пуск – Все программы – Стандартные – Калькулятор.
Чтобы перейти в «нужный» режим следует кликнуть по вкладке Вид и выбрать Программист или нажать комбинацию клавиш Alt+3.
В открывшемся окне можно вводить двоичные, восьмеричные, шестнадцатеричные и десятичные числа, выбрав соответствующий режим. Кроме того можно выполнять различные математические операции между ними.
В дальнейшем, при написании кода программы мы часто будем обращаться к данному калькулятору. Кроме того, опытные программисты любят использовать шестнадцатеричные числа, а нам проще будет понять двоичный код, поэтому калькулятор в помощь)
Десятичные дроби
Примечание: хаки с float могут не работать на Ардуино!
Разбить float в массив бит (unsigned uint32_t)
#include <stdint.h> typedef union {float flt; uint32_t bits} lens_t; uint32_t f2i(float x) { return ((lens_t) {.flt = x}).bits; }
Вернуть массив бит обратно в float
float i2f(uint32_t x) { return ((lens_t) {.bits = x}).flt; }
Быстрый обратный квадратный корень
return i2f(0x5f3759df - f2i(x) / 2);
Быстрый nый корень из целого числа
float root(float x, int n) { #DEFINE MAN_MASK 0x7fffff #DEFINE EXP_MASK 0x7f800000 #DEFINE EXP_BIAS 0x3f800000 uint32_t bits = f2i(x); uint32_t man = bits & MAN_MASK; uint32_t exp = (bits & EXP_MASK) - EXP_BIAS; return i2f((man + man / n) | ((EXP_BIAS + exp / n) & EXP_MASK)); }
Быстрая степень
return i2f((1 - exp) * (0x3f800000 - 0x5c416) + f2i(x) * exp)
Быстрый натуральный логарифм
#DEFINE EPSILON 1.1920928955078125e-07 #DEFINE LOG2 0.6931471805599453 return (f2i(x) - (0x3f800000 - 0x66774)) * EPSILON * LOG2
Быстрая экспонента
return i2f(0x3f800000 + (uint32_t)(x * (0x800000 + 0x38aa22)))
Байт
С развитием компьютеров, появилась потребность в большем количестве значений для байта. В 1963-м году появилась первая редакция семибитной кодировки ASCII. Поэтому байты стали занимать 7 бит. 7 бит, требующиеся для одного символа данной кодировки позволяют использовать 128 значений. В этой кодировке уже были включены строчные латинские символы, и больший набор управляющих и арифметических символов.
Всемирное распространение компьютеров подтолкнуло дальнейшее расширение границ занимаемых байтом. Для различных языков требовалось чтобы таблица символов также могла хранить алфавит того языка, где используется данная ЭВМ. На текущий момент восемь — это последнее и видимо окончательное количество бит составляющих байт. Соответственно байт может принимать 256 значений. По сравнению с таблицей ASCII в. новых таблицах символов — организовалось 128 вакантных мест. Теперь я думаю можно рассказать как значения хранятся в различных кириллических кодировках.
Приставки К, М, Г, Т («кило-», «киби-» и т.д.)
…чтобы измерять большие объемы данных, используют кратные приставки (это как «килограмм»). Привычная же нам приставка «кило-» означает умножение на 1000 (103), но в двоичной системе счисления используют два в десятой степени (210).
Давайте же вместе с сайтом IT-уроки разберемся в этом запутанном вопросе.
История введения двоичных приставок
Для обозначения величины 210=1024 байт, ввели двоичную приставку «К» (именно прописная буква «К»), но в разговорной речи единицу «К» стали называть «кило», что не совсем одно и то же. Чтобы избежать путаницы, ввели названия приставкам:
Т.е. второй слог изменили с привычного на «би», «бинарный».
Но путаница не исчезла, многие расшифровывали «К» и «М» привычными «кило» и «мега». Даже международные стандарты по-разному интерпретировали расшифровку двоичных приставок. Кроме того, производители добавили масла в огонь внесли свой вклад в запутывание ситуации (одни считали 210, другие 103).
В итоге, чтобы окончательно убрать несоответствие, изменили не только названия, но и приставки:
Как Вы думаете, помогло? Конечно же, нет
В обиходе говорят «кило», в программах ОС Windows пишут «К», в Linux обозначают «Ки», производители жестких и оптических дисков пишут «К», а имеют в виду «Ки» и т.д.
Что же делать обычному пользователю?
Если подвести итог всему сказанному, то на сегодняшний день три варианта использования двоичных приставок, их мы и сведем в три таблицы.
1. Обычное использование двоичных приставок
В свойствах файлов почти все программы, да и сама операционная система Windows использует приставку в виде прописной буквы «К», «М», «Г» и т.д. Производители оперативной памяти используют тот же принцип. То есть можно пользоваться следующей таблицей:
Двоичные приставки в ОС Windows и у производителей ОЗУ 1 Кбайт (КБ или KB или Kbyte) = 1024 байт
Эта «К» на самом деле двоичная приставка «киби» (а не «кило», как все говорят).
2. Правильное использование двоичных приставок
В других операционных системах, а также в профессиональных обзорах серьезных ИТ-изданий сразу пишут «Киб», «МиБ», «ГиБ», чтобы не было сомнений, о чем идет речь.
Двоичные приставки в ОС Linux, OS X и в профессиональных обзорах 1 кибибайт (КиБ или KiB или kebibyte) = 1024 байт
3. Использование десятичных приставок
Если используется приставка «кило», «мега», «гига» и т.д., то имеются в виду следующие соотношения:
Десятичные приставки используют производители накопителей (Жесткие диски, флэшки, DVD-диски) 1 килобайт (кБ или kB или kbyte) = 1000 байт
Куда исчезли 70 гигабайт на жестком диске???
Посмотрим, как Windows видит два моих жестких диска 500 ГБ и 1 ТБ:
Жесткий диск 500 ГБ отображается как 465.76 ГБ, а винчестер объемом 1000 ГБ содержит всего 931.51 гигабайт.
Наверное, Вы уже догадались, почему жесткий диск объемом 1 Терабайт в ОС Windows отображается как 931 ГБ, а не 1000.
Так что, не ругайте производителей и уж тем более компьютерную фирму, всё отмерено верно, но разными рулетками
Т.е. 70 гигабайт никуда не делись, просто гибибайт на жестком диске меньше, чем гигабайт.
Не запутались? Тогда еще один пример.
«Почему на флешке меньше места?»
То же самое и с флэш-накопителями. Если Вы посмотрите на свойства своей флэшки, то (к примеру) вместо 16 GB, указанных на корпусе, увидите 14.9 ГБ!!!
На флешке вместо 16 GB — 14.9 ГБ
Теперь Вы знаете, что 1.1 ГБ «потерялся» при пересчете из килобайт в кибибайты.
Кодировки
Итак, чтобы хранить символы не входящие в ASCII, необходимо было придумать новые кодировки. Поскольку до этого таблица ASCII была наиболее подходящей (были и другие), то она и пошла в основу новых кодировок. Поэтому следующие кодировки отличаются только значениями начиная с 80 (hex). Для наглядности оставлю только кириллические символы.
Так выглядела наиболее популярная кодировка под DOS. Примечательно что файлы в этой кодировке до сих пор встречаются. Как правило среди устаревшей архивной информации, в программах WinRar, Блокнот и WordPad, до сих пор есть опции «открыть как текст DOS», впрочем последними двумя мало кто пользуется =).
Кодировка koi8 была примечательна тем, что русские буквы там располагались на позициях английских звуков из нижней половины (т. е. ASCII). Это когда-то давно позволяло смягчить переход со старых серверов понимающие только ascii на новые, что было актуально среди почтовых серверов. Смысл был в том что если отправленное вами письмо приходило на старый сервер, то пользователю оно показывалось как транслит, что позволяло хоть как-то понять текст письма.
Самая популярная у нас в России однобайтная кодировка, на сегодняшний день, это именно «windows-1251». Разумеется популярность её целиком обусловлена популярностью Windows среди других операционных систем. Возможностей кодировки вполне хватает для использования её в широком круге задач. Например движок моего блога, по-умолчанию, использует для работы именно данную кодировку.
Я не могу не упомянуть о кодировке ISO, Удивительно, но несмотря на то что её никто никогда не использовал, эта кодировка является единственной кодировкой имеющей статус стандарта.
На примере данных кодировок видно, как один байт может хранить какое угодно символьное значение русского и английского языков, а также цифр и знаков пунктуации.
Но что делать когда этого не достаточно?
Многобайтные кодировки
Если вам хочется создать кодировку которая бы имела коды одновременно для русского и греческого алфавита? Одним байтом тут не отделаться. Появилась задача разработать кодировку один знак которой может занимать больше чем один байт, так как два байта могут принимать уже 2^16 = 65536 значений, а четыре байта аж 4294967296. Поэтому сначала придумали стандарт кодирования символов — Юникод, который включал бы в себя максимально полный перечень символов которые может принимать один знак.
Первая версия Юникода (Unicode 1991 г.) представляла собой 16-битную кодировку с фиксированной шириной символа; общее число разных символов было 216 (65 536).
Вторая версия Юникода (UCS-2), стала называться UTF-16, она позволяла гораздо расширить количество возможных значений, также используя для символов 16-битные последовательности (т. е. по 2 или по 4 байта на символ).
Кодировка UTF-32 (UCS-4) использует по 32 бита, или 4 байта на хранение одного символа. Строго говоря, стандарт Unicode не описывает символы со значениями выше 2^21, так что хватило бы и трёх байт, на символ, вероятно компьютеры работают несколько быстрее с мелкими блоками памяти кратными двум, или для того чтобы в сектор диска попадало кратное количество символов. Так или иначе это единственная из многобайтных кодировок с постоянной длиной. Помимо недостатка — использования четырёх байт на символ, у неё есть и очевидное преимущество — возможность прямой адресации к N-ному символу. В других кодировках требуется последовательное вычисление позиции каждого символа. Поэтому текстовые редакторы, внутри себя хранят всю информацию в виде UCS-4.
В 1992 году Кеном Томпсоном и Робом Пайком был изобретён формат UTF-8. Он отличается тем, что он ASCII совместим, и значения из таблицы Юникода могут занимать от 1 до 4х символов.
Символы UTF-8 получаются из Unicode следующим образом:
Unicode | UTF-8 | Представленные символы |
---|---|---|
— | ASCII, в том числе английский алфавит, простейшие знаки препинания и арабские цифры | |
— | кириллица, расширенная латиница, арабский, армянский, греческий, еврейский и коптский алфавит; сирийское письмо, тана, нко; МФА; некоторые знаки препинания | |
— | все другие современные формы письменности, в том числе грузинский алфавит, индийское, китайское, корейское и японское письмо; сложные знаки препинания; математические и другие специальные символы | |
— | музыкальные символы, редкие китайские иероглифы, вымершие формы письменности |
Символы, в кодировке UTF-8, могут занимать до шести байт, но Unicode не определяет символов выше , поэтому символы Unicode могут иметь максимальный размер в 4 байта в UTF-8.
“Трюки” с битами
Целые
Установка nго бита
Выключение nго бита
Инверсия nго бита
Округление до ближайшей степени двойки
Округление вниз
Получение максимального целого
Получение минимального целого
Получение максимального long
Умножение на 2
Деление на 2
Умножение на mую степень двойки
Деление на mую степень двойки
Остаток от деления
И так далее
Проверка равенства
Проверка на чётность (кратность 2)
Обмен значениями
Получение абсолютного значения
Максимум из двух
Минимум из двух
Проверка на одинаковый знак
Смена знака
Вернёт 2n
Является ли число степенью 2
Остаток от деления на 2n на m
Среднее арифметическое
Получить mый бит из n (от младшего к старшему)
Получить mый бит из n (от старшего к младшему)
Проверить включен ли nый бит
Выделение самого правого включенного бита
Выделение самого правого выключенного бита
Выделение правого включенного бита
Выделение правого выключенного бита
n + 1
n – 1
Получение отрицательного значения
if (x == a) x = b; if (x == b) x = a;
Поменять смежные биты
Different rightmost bit of numbers m & n
Common rightmost bit of numbers m & n
Десятичные дроби
Примечание: хаки с float могут не работать на Ардуино!
Разбить float в массив бит (unsigned uint32_t)
#include <stdint.h> typedef union {float flt; uint32_t bits} lens_t; uint32_t f2i(float x) { return ((lens_t) {.flt = x}).bits; }
Вернуть массив бит обратно в float
float i2f(uint32_t x) { return ((lens_t) {.bits = x}).flt; }
Быстрый обратный квадратный корень
return i2f(0x5f3759df - f2i(x) / 2);
Быстрый nый корень из целого числа
float root(float x, int n) { #DEFINE MAN_MASK 0x7fffff #DEFINE EXP_MASK 0x7f800000 #DEFINE EXP_BIAS 0x3f800000 uint32_t bits = f2i(x); uint32_t man = bits & MAN_MASK; uint32_t exp = (bits & EXP_MASK) - EXP_BIAS; return i2f((man + man / n) | ((EXP_BIAS + exp / n) & EXP_MASK)); }
Быстрая степень
return i2f((1 - exp) * (0x3f800000 - 0x5c416) + f2i(x) * exp)
Быстрый натуральный логарифм
#DEFINE EPSILON 1.1920928955078125e-07 #DEFINE LOG2 0.6931471805599453 return (f2i(x) - (0x3f800000 - 0x66774)) * EPSILON * LOG2
Быстрая экспонента
return i2f(0x3f800000 + (uint32_t)(x * (0x800000 + 0x38aa22)))
Строки
Конвертировать в нижний регистр
Конвертировать в верхний регистр
Инвертировать регистр
Позиция буквы в алфавите (англ)
Позиция большой буквы в алфавите (англ)
Позиция строчной буквы в алфавите (англ)
Важные страницы
- Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress у проверенных продавцов
- Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
- Полная документация по языку Ардуино, все встроенные функции и макро, все доступные типы данных
- Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
- Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
- Поддержать автора за работу над уроками
- Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту (alex@alexgyver.ru)
Важные страницы
- Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress у проверенных продавцов
- Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
- Полная документация по языку Ардуино, все встроенные функции и макро, все доступные типы данных
- Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
- Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
- Поддержать автора за работу над уроками
- Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту (alex@alexgyver.ru)
Двоичная система и хранение данных
В начале цикла уроков (в уроке о типах данных) мы уже разбирали тему систем исчисления и важность двоичной системы. Давайте коротко вспомним, как это всё работает
Минимальная ячейка памяти, которую мы можем изменить – бит, он принимает всего два значения: 0 и 1. Минимальная ячейка памяти, к которой мы можем обратиться (которая имеет адрес в памяти) – байт, байт состоит из 8-ми бит, каждый занимает свою ячейку (примечание: в других архитектурах в байте может быть больше или меньше бит, в данном уроке речь идёт об AVR и 8-ми битном байте). Таким образом, байт – это элементарный блок памяти, к которому мы можем обратиться и читать/записывать данные, самый младший тип данных в Arduino так и называется – .
Обратившись к байту, мы можем манипулировать битами, из которых он состоит, именно для этого и используются операции с битами. Если мы “включим” все биты в байте, то получится число в двоичной системе, или в десятичной
Здесь нужно вспомнить важность степени двойки – на ней в битовых операциях завязано абсолютно всё. Давайте посмотрим на первые 8 степеней двойки (начиная с 0):
2 в степени | DEC | BIN |
1 | 0b00000001 | |
1 | 2 | 0b00000010 |
2 | 4 | 0b00000100 |
3 | 8 | 0b00001000 |
4 | 16 | 0b00010000 |
5 | 32 | 0b00100000 |
6 | 64 | 0b01000000 |
7 | 128 | 0b10000000 |
Таким образом, степень двойки явно “указывает” на номер бита в байте, считая справа налево (примечание: в других архитектурах может быть иначе)
Напомню, что абсолютно неважно, в какой системе исчисления вы работаете – микроконтроллеру всё равно и он во всём видит единицы и нули. Если “сложить” полный байт в десятичном представлении битов, то мы получим как раз 255: 128+64+32+16+8+4+2+1 = 255
Нетрудно догадаться, что число 0b11000000 равно 128+64, то есть 192. Именно таким образом и получается весь диапазон от 0 до 255, который умещается в один байт. Если взять два байта – будет всё то же самое, просто ячеек будет 16, то же самое для 4 байт – 32 ячейки с единицами и нулями, каждая имеет свой номер согласно степени двойки.
Давайте начнём манипуляции с битами с самого простого – с макро-функций, которые идут “в комплекте” с ядром Arduino.
Шестнадцатеричная система счисления
В программировании микроконтроллеров очень часто пользуются шестнадцатеричными числами. Данная система счисления имеет основание 16, соответственно и 16 различных символов. Первые десять символов 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 заимствованы из десятеричной системы. В качестве оставшихся шести символов применяются буквы A, B, C, D, E, F.
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
Высокая популярность шестнадцатеричной системы счисления поясняется тем, что при отображении одного и того же значения используется меньше разрядов по сравнению с десятичной системой и тем более с двоичной. Например, при отображении 100 используется три десятичных разряда 10010 или 7 двоичных разрядов 11001002 и только 2 шестнадцатеричных разряда 6416.
10010 = 11001002 = 6416
А если записать 1000000, то разница в количестве занимаемых разрядов буде еще более ощутима:
1 000 00010 = 1111 0100 0010 0100 00002 = F424016
Битовые операции
Переходим к более сложным вещам. На самом деле они максимально просты для микроконтроллера, настолько просты, что выполняются за один такт. При частоте 16 МГц (большинство плат Arduino) одна операция занимает 0.0625 микросекунды.
Битовое И
И (AND), оно же “логическое умножение”, выполняется оператором или и возвращает следующее:
0 & 0 == 0 0 & 1 == 0 1 & 0 == 0 1 & 1 == 1
Основное применение операции И – битовая маска. Позволяет “взять” из байта только указанные биты:
myByte = 0b11001100; myBits = myByte & 0b10000111; // myBits теперь равен 0b10000100
То есть при помощи мы взяли из байта 0b11001100 только биты 10000111, а именно – 0b11001100, и получили 0b10000100
Также можно использовать составной оператор
myByte = 0b11001100; myByte &= 0b10000000; // берём старший бит // myByte теперь 10000000
Битовое ИЛИ
ИЛИ (OR), оно же “логическое сложение”, выполняется оператором или и возвращает следующее:
0 | 0 == 0 0 | 1 == 1 1 | 0 == 1 1 | 1 == 1
Основное применение операции ИЛИ – установка бита в байте:
myByte = 0b11001100; myBits = myByte | 0b00000001; // ставим бит №0 // myBits теперь равен 0b11001101 myBits = myBits | bit(1); // ставим бит №1 // myBits теперь равен 0b11001111
Также можно использовать составной оператор
myByte = 0b11001100; myByte |= 16; // 16 - 2 в 4, включаем бит №4 // myByte теперь 0b11011100
Вы уже поняли, что указывать на нужные биты можно любым удобным способом: в бинарном виде (0b00000001 – нулевой бит), в десятичном виде (16 – четвёртый бит) или при помощи макросов или ( даёт 128 или 0b10000000, делает то же самое)
Битовое НЕ
Битовая операция НЕ (NOT) выполняется оператором и просто инвертирует бит:
~0 == 1 ~1 == 0
Также она может инвертировать байт:
myByte = 0b11001100; myByte = ~myByte; // инвертируем // myByte теперь 00110011
Битовое исключающее ИЛИ
Битовая операция исключающее ИЛИ (XOR) выполняется оператором или и делает следующее:
0 ^ 0 == 0 0 ^ 1 == 1 1 ^ 0 == 1 1 ^ 1 == 0
Данная операция обычно используется для инвертирования состояния отдельного бита:
myByte = 0b11001100; myByte ^= 0b10000000; // инвертируем 7-ой бит // myByte теперь 01001100
То есть мы взяли бит №7 в байте 0b11001100 и перевернули его в 0, получилось 0b1001100, остальные биты не трогали.
Битовый сдвиг
Битовый сдвиг – очень мощный оператор, позволяет буквально “двигать” биты в байте вправо и влево при помощи операторов и , и соответственно составных и Если биты выходят за границы блока (8 бит, 16 бит или 32 бита) – они теряются.
myByte = 0b00011100; myByte = myByte << 3; // двигаем на 3 влево // myByte теперь 0b11100000 myByte >>= 5; // myByte теперь 0b00000111 myByte >>= 2; // myByte теперь 0b00000001 // остальные биты потеряны!
Битовый сдвиг делает не что иное, как умножает или делит байт на 2 в степени. Да, это операция деления, выполняющаяся за один такт процессора! К этому мы ещё вернёмся ниже. Посмотрите на работу оператора сдвига и сравните её с макросами и :
1 << 0 == 1 1 << 1 == 2 1 << 2 == 4 1 << 3 == 8 ... 1 << 8 == 256 1 << 9 == 512 1 << 10 == 1024
Да, это возведение двойки в степень!
Включаем-выключаем
Вспомним пример из пункта про битовое ИЛИ, про установку нужного бита. Вот эти варианты кода делают одно и то же:
myByte = 0b11000011; // ставим бит №3 разными способами // по сути - одно и то же myByte |= (1 << 3); myByte |= bit(3); myByte |= _BV(3); bitSet(myByte, 3); // myByte равен 0b11001011
Как насчёт установки нескольких бит сразу?
myByte = 0b11000011; // ставим бит №3 и 4 разными способами // по сути - одно и то же myByte |= (1 << 3) | (1 << 4); myByte |= bit(3) | bit(4); myByte |= _BV(3) | _BV(4); // myByte равен 0b11011011
Или прицельного выключения бит? Тут чуть по-другому, используя и
myByte = 0b11000011; // выключаем бит №1 разными способами // по сути - одно и то же myByte &= ~(1 << 1); myByte &= ~_BV(1); bitClear(myByte, 1); // myByte равен 0b11000001
Выключить несколько бит сразу? Пожалуйста!
myByte = 0b11000011; // выключаем биты №0 и 1 разными способами myByte &= ~( (1 << 0) | (1 << 1) ); myByte &= ~( _BV(0) | _BV(1) ); // myByte равен 0b11000000
Именно такие конструкции встречаются в коде высокого уровня и библиотеках, именно так производится работа с регистрами микроконтроллера.
Вернёмся к устройству Ардуиновских макросов:
#define bitRead(value, bit) (((value) >> (bit)) & 0x01) #define bitSet(value, bit) ((value) |= (1UL << (bit))) #define bitClear(value, bit) ((value) &= ~(1UL << (bit))) #define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit)) #define bit(b) (1UL << (b))
Я думаю, комментарии излишни: макросы состоят из тех же элементарных битовых операций и сдвигов!