- Сборники трюков
- Целые числа
- Проверка на -1
- Бит в байте
- Мин/макс знаковое
- Заполнить битами
- Перемотка бита
- Установка nго бита
- Выключение nго бита
- Инверсия nго бита
- Округление до ближайшей степени двойки
- Получение максимального целого
- Получение минимального целого
- Умножение на 2
- Деление на 2
- Умножение на mую степень двойки
- Деление на mую степень двойки
- Остаток от деления
- Проверка равенства
- Проверка на кратность 2
- Обмен значениями
- Проверка на одинаковый знак
- Смена знака
- Вернёт 2n
- Является ли число степенью 2
- Остаток от деления на 2n на m
- Среднее арифметическое
- Получить mый бит из n (от младшего к старшему)
- Установить mый бит из n (от старшего к младшему)
- Проверить включен ли nый бит
- Выделение самого правого включенного бита
- Выделение самого правого выключенного бита
- Выделение правого включенного бита
- Выделение правого выключенного бита
- Получение отрицательного значения
- Поменять смежные биты
- Быстрое деление на 10 с остатком
- Быстрый обратный квадратный корень
- Целочисленный квадратный корень 16 бит
- Целочисленный квадратный корень 32 бит
- Целочисленный квадратный корень 64 бит
- Развернуть байт (11110000 -> 00001111)
- Float числа
- Строки
Сборники трюков #
- Bit Twiddling Hacks
- awesome-bits - урок ниже содержит в том числе и эти трюки в моём переводе
- Перевод на русский от tproger
Целые числа #
Проверка на -1 #
В программировании очень часто встречаются инструменты, которые возвращают -1 в случае ошибки или "не найдено", т.е. валидным значением считается 0 и положительные числа. Чтобы не сравнивать значение с нулём, например if (val >= 0), можно использовать инверсию: в любом типе данных -1 это все биты - единички. Если инвертировать - получится ноль, if (~val) - проверка на >= 0:
String s = "abc";
if (~s.indexOf('b')) {
// найдено
}
Бит в байте #
uint8_t countBits(uint8_t x) {
x -= (x >> 1) & 0x55;
x = (x & 0x33) + ((x >> 2) & 0x33);
return (x + (x >> 4)) & 0x0F;
}
Мин/макс знаковое #
Определение минимального и максимального значения signed типа:
#define SIGNED_MIN(T) (-(1LL << (sizeof(T) * 8 - 1)))
#define SIGNED_MAX(T) ((1LL << (sizeof(T) * 8 - 1)) - 1)
//SIGNED_MIN(short); -32768
Заполнить битами #
((1ul << len) - 1) << from;
Заполняет len единичек, начиная с бита from, например ((1ul << 3) - 1) << 4 == 0b01110000.
Перемотка бита #
x = ((x >> 1) | (x << 7));
Перематывает за вызов один бит слева направо, то есть формирует последовательность 0b10000000, 0b01000000, 0b00100000, 0b00010000, 0b00001000, 0b00000100, 0b00000010, 0b00000001, 0b10000000, или 128, 64, 32, 16, 8, 4, 2, 1, 128.
Установка nго бита #
x | (1 << n);
Выключение nго бита #
x & ~(1 << n);
Инверсия nго бита #
x ^ (1 << n);
Округление до ближайшей степени двойки #
// работает только с 32 битными числами
uint32_t v;
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
Получение максимального целого #
int maxInt = ~(1 << 31);
int maxInt = (1 << 31) - 1;
int maxInt = (1 << -1) - 1;
int maxInt = -1u >> 1;
Получение минимального целого #
int minInt = 1 << 31;
int minInt = 1 << -1;
Умножение на 2 #
n << 1;
Деление на 2 #
n >> 1;
Умножение на mую степень двойки #
n << m;
Деление на mую степень двойки #
n >> m;
Остаток от деления #
n & 0b1; // на 2
n & 0b11; // на 4
n & 0b111; // на 8
// ...
Проверка равенства #
(a ^ b) == 0;
Проверка на кратность 2 #
(n & 1) == 1;
Обмен значениями #
a = a ^ b ^ (b = a);
Проверка на одинаковый знак #
(x ^ y) >= 0;
Смена знака #
i = ~i + 1;
i = (i ^ -1) + 1;
Вернёт 2n #
1 << n;
Является ли число степенью 2 #
!(n & (n - 1)); // для положительных
Остаток от деления на 2n на m #
m & ((1 << n) - 1);
Среднее арифметическое #
(x + y) >> 1;
((x ^ y) >> 1) + (x & y);
Получить mый бит из n (от младшего к старшему) #
(n >> (m - 1)) & 1;
Установить mый бит из n (от старшего к младшему) #
n = n & ~(1 << (m - 1));
Проверить включен ли nый бит #
x & (1 << n)
Выделение самого правого включенного бита #
x & (-x);
Выделение самого правого выключенного бита #
~x & (x + 1);
Выделение правого включенного бита #
x | (x + 1);
Выделение правого выключенного бита #
x & (x - 1);
Получение отрицательного значения #
~n + 1;
(n ^ -1) + 1;
Поменять смежные биты #
((n & 10101010) >> 1) | ((n & 01010101) << 1);
Быстрое деление на 10 с остатком #
Примерно в 8 раз быстрее, чем val / 10 + val % 10. Можно использовать для разделения числа на десятичные разряды.
struct fdiv10_t {
uint32_t quot;
uint8_t rem;
};
fdiv10_t fdiv10(uint32_t num) {
uint32_t quot = num >> 1;
quot += quot >> 1;
quot += quot >> 4;
quot += quot >> 8;
quot += quot >> 16;
uint32_t qq = quot;
quot >>= 3;
uint8_t rem = uint8_t(num - ((quot << 1) + (qq & ~7ul)));
if (rem > 9) rem -= 10, ++quot;
return fdiv10_t{quot, rem};
}
fdiv10_t res = fdiv10(1234567);
// res.quot == 123456
// res.rem == 7
Быстрый обратный квадратный корень #
float Q_rsqrt( float number ) {
union {
float f;
uint32_t i;
} conv = {number};
conv.i = 0x5f3759df - ( conv.i >> 1 );
conv.f *= 1.5 - number * 0.5 * conv.f * conv.f;
return conv.f;
}
Целочисленный квадратный корень 16 бит #
uint16_t root16(uint16_t x) {
uint16_t m = 0x4000, y = 0, b;
while (m != 0) {
b = y | m;
y >>= 1;
if (x >= b) {
x -= b;
y |= m;
}
m >>= 2;
}
return y;
}
Выполняется 10 мкс (AVR) против 37 мкс sqrt (быстрее).
Целочисленный квадратный корень 32 бит #
uint16_t root32(uint32_t x) {
uint32_t m = 0x4000ul << 16, y = 0, b;
while (m != 0) {
b = y | m;
y >>= 1ul;
if (x >= b) {
x -= b;
y |= m;
}
m >>= 2ul;
}
return y;
}
Выполняется 46 мкс (AVR) против 38 мкс sqrt.
Целочисленный квадратный корень 64 бит #
uint32_t root64(uint64_t x) {
uint64_t y = 0, m = 1ull << 62;
while (m > x) m >>= 2;
while (m != 0) {
if (x >= y + m) {
x -= y + m;
y = (y >> 1) + m;
} else {
y >>= 1;
}
m >>= 2;
}
return y;
}
Выполняется 466 мкс (AVR) против 45 мкс sqrt.
Развернуть байт (11110000 -> 00001111) #
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
Float числа #
Разбить в массив бит (unsigned uint32_t) #
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)));
Строки #
Символ в число #
x & 0xf;
// '0' & 0xf == 0;
// '9' & 0xf == 9;
Байт в HEX #
char _encode(uint8_t b) {
return (b < 10) ? (b + '0') : (b + 'a' - 10);
}
uint8_t b = 123;
char hex[2];
hex[0] = _encode(b >> 4);
hex[1] = _encode(b & 0xF);
// hex == "7b"
В нижний регистр #
(x | ' ');
// ('a' | ' ') == 'a';
// ('A' | ' ') == 'a';
В верхний регистр #
(x & '_');
// ('a' & '_') == 'A';
// ('A' & '_') == 'A';
Инвертировать регистр #
(x ^ ' ');
// ('a' ^ ' ') == 'A';
// ('A' ^ ' ') == 'a';
Позиция в алфавите (англ) #
(x & "\x1F");
// ('a' & "\x1F") == 1;
// ('B' & "\x1F") == 2;
Позиция большой буквы в алфавите (англ) #
(x & '?') или (x ^ '@');
// ('C' & '?') == 3;
// ('Z' ^ '@') == 26;
Позиция строчной буквы в алфавите (англ) #
(x ^ '`');
// ('d' ^ '`') == 4;
// ('x' ^ '`') == 24;
Полезные страницы #
- Набор GyverKIT – наш большой стартовый набор Arduino, продаётся в России
- Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress
- Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту ([email protected])
- Поддержать автора за работу над уроками