Страницы

Поиск по вопросам

Показаны сообщения с ярлыком битовые-операции. Показать все сообщения
Показаны сообщения с ярлыком битовые-операции. Показать все сообщения

вторник, 17 марта 2020 г.

Битовые операции - алгоритм

#битовые_операции #алгоритм #javascript


Доброго времени суток. 
Встала одна интересная задача.
Есть модели данных. Каждая модель имеет свой битовый идентификатор. Например:
User: 0x1
Organization: 0x2
Points: 0x4
.....

У любой модели есть список параметров и есть функция getData(flag), которая которая
возвращает параметры по битовой маске.
Пример:
Model User {
   TYPE: items.TYPE.USER; // 0x1
   getFlags: function() {
      Id: 0x1,
      Name: 0x2,
      Owner: 0x4....
   }
   getData(flag) {
      var dl = this.getFlags();
      var a = {};
      for(var i in dl) {
         var tf = dl[i];
         if(fl & flag) {
            a[i] = this['get' + i]();
         }
      }
      return a;
   }
}

Model Organization { ... }

Задача - реализовать функцию, которая по 2-м параметрам (битовым маскам) будет возвращать
items-ы с необходимой информацией.
Пример:
/**
 * flag - битовая маска модулей
 * info битовая маска информативности по модулям
*/
getItems(flag, info) { ...; return items; };

Например, задача выцепить пользователей, но чтобы мне вернулись только их ID и выцепить
организации, но только чтобы вернулись имена.
Вопрос - как грамотно составить маску info?
flag = User.TYPE + Organization.TYPE
info - ?

И встаёт дополнительный вопрос - как в JavaScript можно оперировать большими битовыми
флагами ? Большие числа будут сокращаться же :-( 
<< 100000000000000000111
>> 100000000000000000000
    


Ответы

Ответ 1



Если я правильно понял задачу, то flag = User.TYPE | Organization.TYPE; // не +. Сейчас разницы нет, но на будущее info = {}; info[User.TYPE] = User.getFlags().Id | User.getFlags().Name; info[Organization.TYPE] = Organization.getFlags().Name; Почему так: внезапно в JS нельзя инициализировать объект с переменными в названиях свойств (в кратком формате). Я только что это узнал %) Объект формата type => mask выбрал потому, что, судя по всему, вы собираетесь работать с большими масками. В другом случае это может быть простой массив масок ((User.TYPE << 16) | User.getFlags().Id) с разбором в функции. По второй части вопроса - динамическая типизация JS + разноразрядность осей съест вам голову. Для работы с "большими масками" лучше использовать массивы, а при проверке передавать еще смещения Т.е. маска в 64 бита - это массив 2 х int32 var mask64 = [ 0x00020000, 0x00200080 ]; , а проверка 62-го бита - это test(0x00000002 /*30-й бит*/ , 1/* смещение в 32 бит */) Как-то так вроде.

понедельник, 2 марта 2020 г.

Signed Int32 из двух байт

#java #c_sharp #битовые_операции


Есть строка из Java приложения, которая формирует signed INT из двух байт массива:

final int size = array[0] & 0x00FF | array[1] << 8;


где, array[0] равно 0x08, array[1] равно 0xEE. Этот код формирует число -4600.

Но C# формирует совсем иное число из аналогичного кода:

int size = array[0] & 0x00FF | array[1] << 8;


Но этот код формирует число 60936.

Подскажите, в чём соль между этими языками и как решить такую проблему.
    


Ответы

Ответ 1



В этой строке final int size = array[0] & 0x00FF | array[1] << 8; значение выражения записывается в int (4 байта). Это значит, что все операнды преобразовываются в четырехбайтовые значения. Расширение разрядности в Java происходит путем копирования старшего бита в исходном числе на расширяемые биты. Итого у Вас было final int size = 0x08 & 0x00FF | 0xEE << 8; или в двоичном виде final int size = 0000_1000b & 0000_0000_0000_0000_0000_0000_1111_1111b | 1110_1110b << 8; теперь, что получается при расширении final int size = 0000_0000_0000_0000_0000_0000_0000_1000b & 0000_0000_0000_0000_0000_0000_1111_1111b | 1111_1111_1111_1111_1111_1111_1110_1110b << 8; В c# такого копирования старшего бита не происходит. (скорее всего там приводится к типу не операнды, а итоговый результат) Если Вы хотите на Java избежать такого расширения, то применяйте к каждой байтовой переменной операцию побитового И с 0xFF final int size = array[0] & 0xFF | (array[1] & 0xFF) << 8; тогда при расширении получится final int size = 0000_0000_0000_0000_0000_0000_0000_1000b & 0000_0000_0000_0000_0000_0000_1111_1111b | ( 1111_1111_1111_1111_1111_1111_1110_1110b & 0000_0000_0000_0000_0000_0000_1111_1111b ) << 8; final int size = 0000_0000_0000_0000_0000_0000_0000_1000b | 0000_0000_0000_0000_0000_0000_1110_1110b << 8;

понедельник, 24 февраля 2020 г.

Кроссплатформенность битовых операций

#cpp #битовые_операции #кроссплатформенность


Как добиться кроссплатформенности при сериализации, работе напрямую с битами, составления
пакетов для отправки между классами при условии, что битовые манипуляции должны быть
верны при little endian и big endian.
    


Ответы

Ответ 1



При общении в пределах одной системы учитывать порядок байт нет смысла. все потоки, процессы и т.п. работают с одним порядком. Вопрос встает только при передаче информации на другую платформу. Т.е. данные должны быть приведены к заранее оговоренному порядку байт только при передаче по сети или при записи в файлы, которые могут быть впоследствии перенесены на другую систему.

Ответ 2



В полях структур, используемых для обмена, храните данные в сетевом формате (network order, подробнее можете посмотреть здесь). Для преобразования данных между сетевым форматом и форматом хоста можно использовать функции htons()/htonl()/ntohs()/ntohl() из Berkeley sockets API.

Ответ 3



Внутри байта биты всегда идут слева направо от старшего к младшему, независимо от принятого порядка байтов в системе. То же самое касается операндов у операторов >> <<, даже если это числа, состоящие из больше чем 1-го байта. То есть int i=4; i>>=1; i всегда будет равно 2. Для кроссплатформенной (де)сериализации можно использовать htons()/htonl()/ntohs()/ntohl()

вторник, 18 февраля 2020 г.

Эффективные ли побитовые операции в Java?

#java #производительность #битовые_операции


Если брать нативные языки на подобие "C++" или "C", то там понятен выигрыш в производительности
напрямую играться с регистрами, но если брать язык, где все крутится на виртуалке JVM
- не совсем понятно, в чем мы можем выиграть в производительности и выиграем ли вообще??

Как работают битовые операции под капотом JVM?
Или в джава просто реализованы побитовые операции для лучшей переносимости тех же
алгоритмов например с плюсов или си?

И в каких реальных случаях в java мире нам будет интересно применять на практике
битовые операции?
    


Ответы

Ответ 1



в чем мы можем выиграть в производительности В производительности перед операциями с аналогичными результатами без применения битовых операций. Если вы думаете, что JVM делает всё медленнее, то ведь не только битовые операции страдают, верно? в каких рельных случаях в java мире нам будет интересно применять на практике битовіе операции? В тех же, что и в остальном мире Если брать нативные языки на подобие "с++" или "С" то там понятен выиграш в производительности напрямую игратся с регистрами, но если брать язык где все крутится на виртуалке JVM JVM - это не интерпретатор, это машина, транслирующая java-байткод в машинный код, соответствующий спецификации. В результате битовых операций будут вызываться ровно те же инструкции процессора, что и на ассемблере. Конкретно в самой битовой операции никакой потери производительности нет, основной "замедлитель" относительно условного си - это наличие сборки мусора и присущих stop-the-world пауз.

воскресенье, 16 февраля 2020 г.

Эффективные ли побитовые операции в Java?

#java #производительность #битовые_операции


Если брать нативные языки на подобие "C++" или "C", то там понятен выигрыш в производительности
напрямую играться с регистрами, но если брать язык, где все крутится на виртуалке JVM
- не совсем понятно, в чем мы можем выиграть в производительности и выиграем ли вообще??

Как работают битовые операции под капотом JVM?
Или в джава просто реализованы побитовые операции для лучшей переносимости тех же
алгоритмов например с плюсов или си?

И в каких реальных случаях в java мире нам будет интересно применять на практике
битовые операции?
    


Ответы

Ответ 1



в чем мы можем выиграть в производительности В производительности перед операциями с аналогичными результатами без применения битовых операций. Если вы думаете, что JVM делает всё медленнее, то ведь не только битовые операции страдают, верно? в каких рельных случаях в java мире нам будет интересно применять на практике битовіе операции? В тех же, что и в остальном мире Если брать нативные языки на подобие "с++" или "С" то там понятен выиграш в производительности напрямую игратся с регистрами, но если брать язык где все крутится на виртуалке JVM JVM - это не интерпретатор, это машина, транслирующая java-байткод в машинный код, соответствующий спецификации. В результате битовых операций будут вызываться ровно те же инструкции процессора, что и на ассемблере. Конкретно в самой битовой операции никакой потери производительности нет, основной "замедлитель" относительно условного си - это наличие сборки мусора и присущих stop-the-world пауз.

пятница, 14 февраля 2020 г.

Какой порядок выполнения операций и почему?

#c_sharp #битовые_операции #операторы


static class Program
{
  static void Main()
  {
   var x=3;
   var y=(++x)*(x++)|4/2^2;
   Consoley.Write(y);
 }
} 

    


Ответы

Ответ 1



В соответствии с приоритетами слева направо: (++x) * (x++) | 4 / 2 ^ 2 ^ ^^^^^ +--------------------- x=4, returns 4 | ^^^^^--------------- x=5, returns 4 `--------------------- 4*4=16 ^ | ^ | ^-+--------- 4 | | ^------- 2 | `--------- 4/2=2 | ^----- 2^2=0 | | ^--- 2^2=0 | `----- 2^2=0 `------------- 16|0=16 PS: Надеюсь, не ошибся.

четверг, 13 февраля 2020 г.

Что такое разряд?

#битовые_операции


Что такое разряд и чем он отличается  бита?
    


Ответы

Ответ 1



Разряд — это структурный элемент представления чисел в позиционных системах счисления. Бит — единица измерения количества информации, равная одному разряду в двоичной системе счисления.

понедельник, 10 февраля 2020 г.

Непонятная битовая операция

#cpp #c #битовые_операции


Что может означать операция i = i & (i+1) в реализации дерева отрезков?
    


Ответы

Ответ 1



Превращает все завершающие единичные биты в нулевые; если таковых нет - просто возвращает исходное значение. i 0011011111 00101100100 11010101000 i+1 0011100000 00101100101 11010101001 i&(i+1) 0011000000 00101100100 11010101000 Для чего именно это сделано в конкретной программе - это уж смотрите, где и как это действие использовано...

суббота, 8 февраля 2020 г.

Побитовое И в C#

#c_sharp #битовые_операции


Мне нужно перевести подобную строку кода -

if (a & b) 
{

}


Где a - int, b - число из enum.

В C++ имеется оператор "побитовое И". Можно ли подобное сделать на С#?
    


Ответы

Ответ 1



Запросто. В C#, как и в C++, за побитовое И отвечает оператор &. Ваш пример будет выглядеть следующим образом. Если a и b имеют одинаковый значащий тип (например int, или какой-то enum): if ((a & b) != 0) { } Если b имеет типом какой-либо enum: if ((a & (int)b) != 0) { }

пятница, 31 января 2020 г.

Побитовое сравнение

#c_sharp #битовые_операции


Объясните пожалуйста на пальцах. У меня есть маска в двоичном представлении 0000
0100, я хочу во время когда пользователь вводит число, например : 4 что в двоичном
равно маске, получать true, во всех остальных случаях false. Делаю вот так :  

public static bool isTrue(byte num)
{
    byte result = (byte)(num & MASK_DAY); //Константа = 0x04;

    if(result == 0)
    {
        return false;
    }

    return true;
}  


В этом случае я получаю всегда true когда бит другого числа, 2 разряда, равен 1,
а как мне сделать что бы я получал true только тогда когда 2-е разряды обоих чисел
совпадают, а все остальное равно 0? Или я не правильно понимаю работу с битами?
    


Ответы

Ответ 1



Битовые маски применяются для того, чтобы проверить в числе только интересующие нас биты. А на остальные не обращать внимания. Например: проверка, что в числе установлены те биты, которые установлены в MASK_DAY. Значение остальных бит нас не интересует if (num & MASK_DAY == MASK_DAY) в числе не установлены те биты, которые установлены в MASK_DAY if (num & MASK_DAY == 0) проверка, что все не установленные биты MASK_DAY сброшены и в num if (num | MASK_DAY == MASK_DAY) проверка, что все установленные биты MASK_DAY установлены и в num, а не установленные - сброшены if (num == MASK_DAY)

Ответ 2



Повторять уже написанное не буду, но некоторые уточнения внесу. 1. Битовые (поразрядные) операции в качестве результата возвращают число. это означает, что если вы, например, выполнили операцию byte res = 0x04 & 0x0f 00000100 //0x04 &&&&&&&& 00001111 //0x0f ======== 00000100 //0x04 то переменная res будет содержать 0x04 2. В C# в условном операторе if допустимы только логические выражения Это означает, что мы не можем использовать результат поразрядной операции (число) в качестве условия для if, поэтому нам необходимо использовать логическую (результат типа bool [true/false]) операцию сравнения == для проверки, что после применения поразрядной операции получилось нужное число. В большинстве случаев достаточно простых логических операций сравнения. Битовые операции имеет смысл использовать тогда, и только тогда, когда вас интересует состояние конкретных бит в числе, например в случае упаковки нескольких логических переменных в одно число с целью экстремальной экономии памяти.

вторник, 28 января 2020 г.

Объясните логику работы выражения

#java #cpp #битовые_операции


Объясните пожалуйста, как работает это выражение !(a & (a - 1))
В плюсах совершенно не понимаю, в Java ! нельзя применять к int.

        private int isPow2(int a)
        {
          return !(a & (a - 1));
        }

    


Ответы

Ответ 1



Возьмем два числа A и B. Выражение A & B будет равно 0 только тогда, когда числа А и B не содержать единичных бит на одних и тех же позициях. Если (a & (a - 1) = 0, то a и a-1 не содержат общих единичных бит. Давайте возьмем число а и попробуем вычесть одну единицу. Когда мы отнимаем единицу, смотрим на младший бит. если он равен 1 то мы просто заменяем его на 0. Но если там стоит 0, то мы должны заимствовать из старшего бита. Мы заменяем каждый бит с 0 на 1 до тех пор, пока не найдем бит, равный 1. Затем вы инвертируем найденную единицу в ноль. То есть чтобы получить ноль при выполнении операции &, нам нужно, чтобы младшие нули в a соответствовали единицам в a - 1, а последний(и единственный) единичный бит в a(если существует) стал бы нулем в a - 1 - только таким образом во всех позициях будут отсутствовать единичные биты. Это условие выполняется только, если число является степенью двойки, например 1000 & 0111 = 0 10000 & 01111 = 0 число равно 0 0000 & 1111 = 0 Поэтому (a & (a - 1)) = 0, если а - степень двойки или ноль

Ответ 2



Операция a&(a-1) обнуляет крайний справа единичный бит или дает 0, если такового нет: a = 01011000 a-1 = 01010111 a&(a-1) = 01010000 Таким образом, для всех a, являющихся степенями двойки (у которых только один бит - единичный, остальные - нулевые) и 0, выражение дает 0, для чисел, таковыми не являющимися - ненулевое значение. ! инвертирует полученное логическое значение (0 - false, не нуль - true), так что ваша функция дает 1, если a - степень двойки или нуль, и нуль в противном случае.

суббота, 11 января 2020 г.

Побитовый сдвиг влево, проблемка

#javascript #php #битовые_операции


Переписываю некий код с яваскрипта на пхп, должен работать идентично.

Eже разобрался с побитовым ИЛИ, яваскрипт сравнивает числа в 32-битном представлении.
Теперь проблемка с побитовым сдвигом влево.

js 30 << 30 возвращает -2147483648
php 30 << 30 возвращает 32212254720


Хорошо, первые 32 бита, 32212254720 % (2 ** 32), получим 2147483648, почти похоже
на результат js.

Пытаюсь воссоздать логику js, число 30 в двоичном представлении

11110
00000000000000000000000000011110


Сдвигаю влево на 30 бит, получаю

00000000000000000000000000011110000000000000000000000000000000


Оставляю 32 бита

10000000000000000000000000000000


Конвертирую в десятичное

2147483648


Но откуда в js берется знак минус?

PS

Всем спасибо за помощь, написал 3 функции | << >>>, а потом нашел готовую библиотеку
https://github.com/simaguo/javascript-bitwise-operators
    


Ответы

Ответ 1



Побитовые операторы в JavaScript работают с 32-битными целыми числами в их двоичном представлении. Число 30 в двоичной форме это 0b00000000000000000000000000011110 (0b - это просто префикс, говорящий о том, что данное за ним число записано в двоичной форме. Так вот, в тридцати двух битном представлении крайняя левая цифра 0 (сразу после 0b и тридцать вторая если считать справа!) - что соответствует знаку + числа идущего за префиксом). Сдвигаем это число на 30 позиций влево 30 << 30 получаем в десятичном виде -2147483648, а двоичном (32-х битном) 0b10000000000000000000000000000000, где старшая цифра (после префикса 0b) единица, что соответствует знаку минус.

Ответ 2



Из курса дискретной математики, помню: Для записи чисел в памяти компьютера используется различная система двоичных кодов... Существует 3 вида: 1)Прямой 2)Обратный 3)Дополнительный (Коды) Ваш компьютер использует для записи чисел - дополнительный код. Это не обычный двоичный код, в нем если первый знак является 1, то это отрицателеное число(то есть числа начинающиеся со знака 1, отрицательные числа) Кстати Вы встретились с очень полезной ошибкой, в плане ваших знаний) Теперь знаете больше, мой совет кроме изучения языков, рассмотрите как работает ваш компьютер (это не в коем случае не наставления, просто совет))) Удачи!

Ответ 3



Ты осуществляешь операции над целыми числами со знаком. Первый бит отвечает за знак. В джаваскрипте операция js 30 << 30 возвращает 10000000000000000000000000000000 - это число в обратном коде (тебе написали выше) соответствует -2147483648 в десятичной. В пхп у тебя 64-битное слово, поэтому ты получается двоичное число с нулем в первом бите 000000000000000000000000000011110000000000000000000000000000000 что соответствует 32212254720 в десятичной. Чтбы перепроверить удобно использовать программисткий калькулятор виндовс. Если ты хочешь выполнить такую операцию на джаваскрипте - тебе нужно 64-битное целое. Используй node-int64 или goog.math.Long.html из closure-library

среда, 1 января 2020 г.

Наличие 9 бита в байте в современных компьютерах

#ассемблер #битовые_операции


Вырезка из книги Питера Абеля: 


  Группа  из  девяти битов представляет собой  байт; восемь
            битов которого содержат данные и один бит -  контроль на чет
            ность.


В нынешних компьютерах до сих пор это используется? И можно ли как-нибудь повлиять
на него программно/использовать его (при его наличии) или это чисто машинная часть?
    


Ответы

Ответ 1



Не факт, что именно такая реализация. Но сам принцип: хранение избыточности с целью обнаружения (и, возможно, коррекции) ошибок — да, по-прежнему используется, в чипах оперативной памяти с ECC, используемой преимущественно на серверах. Такая память заметно дороже, чуточку медленнее и должна поддерживаться материнской платой и процессором. Реализация аппаратная: избыточность считается железкой. Коррекция, если возможна (один бит?) происходит тоже в железе, но система уведомляется об ошибке (на x86 через machine check exception) и может обработать событие программно на любом уровне. Если невозможно, то происходит только уведомление. Linux, к примеру, резко убивает процесс, использовавший страницу с умершей памятью и не использует эту страницу в дальнейшем. Способы ручного доступа к избыточным битам мне неизвестны. Подозреваю, что даже если они существуют, то скорее с отладочными целями, и использоваться конечными пользователями не должны.

вторник, 31 декабря 2019 г.

Как работает оператор ~ инверсии в java?

#java #битовые_операции


Есть код:

   for(int i = -2; i<2; i++)
    System.out.printf("Инверсия %d даст %d. \n", i, ~i );


На выходе:

Инверсия -2 даст 1. 
Инверсия -1 даст 0. 
Инверсия 0 даст -1. 
Инверсия 1 даст -2.


Оператор инверсии похоже реализован так:

Operator ~ (int i){
return -i-1;
}


Но пишут, что инверсия инвертирует нули в единицы. Хорошо, для -2 (инт для упрощения
4 бита берём) 1010, первый бит - знак минус. Значит инверсия будет 0101, то есть 5.
 Дополнительный код: 0101 + 1 = 0110 - итого 6. Короче, никак у меня в теории не удаётся
инвертировать код 

Вопрос: Как на самом деле (объясните на единицах и нулях) работает этот проклятый
оператор? И зачем разработчики языка сделали его работу именно такой?
    


Ответы

Ответ 1



Инверсия является побитовой операцией и преобразует хранящиеся в памяти единицы в нули и нули в единицы. Чтобы понять почему она так работает с типом int нужно изучит структуру представления примитивного типа int в памяти. Примитивный тип int состоит из четырех байт. Старший бит старшего байта отвечает за знак числа, остальные - за значение. Отрицательные числа хранятся в дополнительном коде. Разберем пример для числа -2: System.out.println(Integer.toBinaryString(-2)); //11111111 11111111 11111111 11111110 - битовое представление числа -2 после операции побитовой инверсии мы получим: 00000000 00000000 00000000 00000001, что эквивалентно десятичной 1 Таким образом для корректного использования побитовых операций важно знать внутреннюю структуру обрабатываемых данных и при необходимости работать только с нужной частью битов используя маски и сдвиги. Необходимо учитывать и то, что другие целочисленные типы при многих операциях так же приводятся к типу int.

Ответ 2



Отрицательные числа компьютер хранит в дополнительном коде. С точки зрения математики, дополнительный код - это кольцо вычетов по модулю 2N. Не пугайтесь, это страшное слово означает всего лишь, что к отрицательным числам добавляется 2N, где N - число разрядов. К примеру, 32х-битное число -1 в памяти хранится как 232-1 = 4294967295. Или, в двоичном виде, 11111111 11111111 11111111 111111112. Аналогично, число -2 в памяти хранится как 232-2 = 11111111 11111111 11111111 111111102, а число -3 - как 232-3 = 11111111 11111111 11111111 111111012. Легко видеть, что инверсия одного бита с точки зрения математики - это вычитание его из 1: 1-1=0, а 1-0=1. Инверсия же всех 32х бит числа - это вычитание их его из 32х единиц. Но, как уже было показано выше, число состоящее из 32х единиц - это -1. Таким образом, математически вы правы, оператор инверсии действительно работает так как вы написали: ~x = -1-x Но не следует думать что эта формула - его реализация. Это всего лишь его математическое свойство, а реализован он по определению - как инверсия всех бит числа. Кстати, если перенести единицу в другую часть, получится более интересная формула: ~x + 1 = -x Интересна эта формула тем, что является реализацией оператора "унарный минус" в процессоре. Именно так процессоры вычитают целые числа: x - y = x + ~y + 1

Зачем нужны побитовые операторы и что они фактически делают в Си?

#c #битовые_операции


Здравствуйте!
Объясните, пожалуйста, для чего нужны побитовые операторы и каков принцип их работы?
Я уже несколько раз перечитывал главу K&R и читал в сети, но не пойму их.
Если можно, с практическими примерами.
Спасибо за понимание.    


Ответы

Ответ 1



Принцип работы предельно прост: идёт работа с битами целых чисел. Есть, например, число 10, оно в двоичной будет 1010, значит если это int (4 байта, 32 бита), то это будет: 0000 0000 . 0000 0000 . 0000 0000 . 0000 1010 Есть ещё, например, число 7. Оно будет равно: 0000 0000 . 0000 0000 . 0000 0000 . 0000 0111 Можно произвести конъюнкцию 7 & 10, т.е. поставить эти числа друг над другом и провести конъюнкцию каждого бита одного числа с соответствующим ему битом другого числа. Будет: 0000 0000 . 0000 0000 . 0000 0000 . 0000 0010 Такая же логика с "или", т.е. '|' и со "сложением по модулю 2", т.е. "^". В инете куча инфы, разумеется. Можете прочитать ещё про сдвиг (bitwise shift). Используется в комбинаторике, есть много примеров, например в алгоритме генерации множества всех подмножеств (используется и "сдвиг" и "побитовое и"). Или, может быть тоже будет интересно разобраться: бинарный алгоритм нахождения НОДа двух чисел

Ответ 2



Как следует из названия они изменяют/проверяют один или несколько бит в машинном представлении целых двоичных (long long, long, int, short, char) чисел. Например для int x; if (x & 1) // нечетное х = (х+7) & ~7; // сделаем его ближайшим большим кратным 8 И т.п. Для понимания этих операций необходимо понимание представления чисел в машинной памяти в битовом представлении. После этого Вы сами придумаете как и когда их применять. Весьма распространено их использование для манипуляции битовыми массивами, где отдельные биты плотно упакованы в массив байт (или слов). Например массив для 8000000 бит будет занимать 1000000 байт памяти.

Ответ 3



Битовые операции.

воскресенье, 29 декабря 2019 г.

Побитовые операции - как получить значение определенного бита?

#c #delphi #битовые_операции


Здравствуйте.
Как получить значение определенного бита в байте?
Допустим мы имеем байт с битами вида:
00110101

Как можно получить значение 4 или 5 бита?
Пример можно показать на любом языке, предпочтительнее C или Delphi.
    


Ответы

Ответ 1



Если вас интересует буквальное значение бита (т.е. 0 или 1), то в языке С значение i-того бита числа n можно получить как (n >> i) & 1u Если вас интересует взвешенное значение бита (т.е. 0 или 8 для бита номер 3), то в языке С значение i-того бита числа n можно получить как n & (1u << i) (Подразумевается нумерация с нуля от младших битов к старшим.)

Ответ 2



Функция на Delphi для проверки, установлен ли конкретный бит в 32-х битном числе: function IsBitSet(const AValue: Cardinal; const ABit: Byte): Boolean; begin Result := (AValue and (1 shl ABit)) <> 0; end; Операция (1 shl ABit) (порязрядный сдвиг значения целого числа влево, на указанное число бит) генерирует число, в котором установлен в 1 только один бит именно в той позиции которая нас интересует. Это число называется маской. Далее, применяется логическая операция AND к входному значению и маске, в результате чего получается ещё одно число, которое либо равно 0 (все биты установлены в 0), либо равно маске, т.е. установлен только один бит. Соответственно, сравнив результат с нулём можно сделать вывод о том, установлен искомый бит или нет. Картинка, поясняющая работу логической операции AND: Результирующий бит считается установленным только если у обоих операндов соответствующий бит так же установлен. В вашем примере, при поиске 4-го бита, будет производится вот такое сложение: 00110101 - входное значение 00001000 - маска -------- 00000000 - результат = 0, т.е 4-й бит не установлен

суббота, 28 декабря 2019 г.

Как склеить 4 unsigned char в один unsigned int

#cpp #битовые_операции


Объясните, пожалуйста, как использовать битовый сдвиг для того, чтобы из четырёх
переменных unsigned char получить одну unsigned int, затем проделать обратное действие.
    


Ответы

Ответ 1



Последовательно в младшую часть int кладем очередной байт и сдвигаем влево на 8 бит. Что бы положить 1 байт в младшую часть используем логическое ИЛИ: unsigned char c1=5,c2=10,c3=98,c4=67; unsigned int I; I=c4; // c4 в младших 8и битах, остальные 0 I<<=8; // Сдвигаем int влево на 8 бит. Младшие 8 бит становятся 0, c4 становится в 9-16 битах. I|=c3; // Логическое ИЛИ заменяет 0 биты на те, что в байте c3 I<<=8; I|=c2; I<<=8; I|=c1; // Аналогично кладем остальные байты Для обратного действия сдвигаем int так, что бы нужный нам байт был самым младшим и маскируем остальные биты логическим И: c1=I & 0xFF; c2=(I>>8) & 0xFF; c3=(I>>16) & 0xFF; c4=(I>>24) & 0xFF; Только следим за порядком байт, на разных архитектурах числа в int принято класть по разному.

Ответ 2



Если вам нужно делать только такую операцию упаковки/распаковки то могу предложить вообще не используя битовые операции. unsigned int UI = 0x12345678; char *CC = (char *)(&UI); for (int i=3;i>=0;i--) cout << (int)CC[i]<<" "; unsigned char NC[4] = {120,86,52,18}; unsigned int TI = *(unsigned int *)(NC); cout << TI; Идея основана на явном преобразовании указателя. https://ideone.com/1P4tfc запускаемый пример.

Ответ 3



Например, вот так: int main() { unsigned char ch1 = 0x1; unsigned char ch2 = 0x2; unsigned char ch3 = 0x3; unsigned char ch4 = 0x4; unsigned int value = ch1; value <<= 8; value |= ch2; value <<= 8; value |= ch3; value <<= 8; value |= ch4; } Или чуть более общий вариант решения: #include const size_t BYTE_COUNT_IN_INT = 4; const size_t BITS_COUNT_IN_BYTE = 8; unsigned int getIntFromCharsArray(const std::array& charsArray) { unsigned int result = charsArray[0]; for (size_t i = 1; i <= BYTE_COUNT_IN_INT - 1; ++i) { result <<= BITS_COUNT_IN_BYTE; result |= charsArray[i]; } return result; } std::array getCharsArrayFromInt(unsigned int value) { std::array result; for (size_t i = 0; i <= BYTE_COUNT_IN_INT - 1; ++i) { result[BYTE_COUNT_IN_INT - i - 1] = value; value >>= BITS_COUNT_IN_BYTE; } return result; } int main() { const std::array charsArray = { 0x1, 0x2, 0x3, 0x4 }; unsigned int intFromCharsArray = getIntFromCharsArray(charsArray); auto charsArrayFromInt = getCharsArrayFromInt(intFromCharsArray); }

Ответ 4



Дополню. Если нужна именно конвертация и можно не использовать битовые сдвиги, то самое простое решение через union: union converter { unsigned int number; unsigned char bytes[4]; }; Число в байты: converter c; c.number = 123; // c.bytes[0] == 0xD2 // c.bytes[1] == 0x04 // c.bytes[2] == 0x00 // c.bytes[3] == 0x00 Байты в число: converter c; c.bytes[0] = 1; c.bytes[1] = 2; c.bytes[2] = 3; c.bytes[3] = 4; // c.number == 0x04030201 (big-endian)

Число, кратное 2^n

#алгоритм #математика #битовые_операции


Из float A, которое может принимать любое значение, нужно получить число, кратное 2n.
Например, если A = 345.53;, то результат должен быть равен 256.

Пока что в голову ничего, кроме использования условного оператора, не приходит, но
это не совсем подходит, но думаю, есть вариант экономнее, так как выполняется это при
рендеринге, что и не должно влиять на fps.
    


Ответы

Ответ 1



Простое решение "в лоб" на C (для сравнения скорости): #include #include int main() { volatile double A=345.53; volatile double r; for(int i=100000000; i--;) r= exp2(floor(log2(A))); printf("%f\n", r); } volatile поставил чтобы было честное вычисление, а не результат спрогнозированный оптимизатором. Проверил на 2х машинах: Celeron(R) Dual-Core CPU T3100 @ 1.90GHz Intel(R) Core(TM) i5-2500K CPU @ 3.30GHz Время счёта 17.812 и 9.288 секунд соответственно. Быстрый переносимый вариант (только тело цикла): int exp; frexp(A, &exp); r= ldexp(.5, exp); Время счёта 6.240 и 1.768 секунды. Вариант зависимый от представления в расчёте на то, что FLT_RADIX==2: r= scalbln(1, ilogb(A)); Последний вариант у меня оказался на селероне немного медленнее -- 7.488 секунд, а на коре немного быстрее -- 1.204 секунды.

Ответ 2



Степень двойки: (long) (log(A)/log(2)); Ну и для малых степеней используем сдвиг, да. Просто "float A" должно натолкнуть на мысль, что FLT_MAX ≈ 3.4E+38...

Ответ 3



Как верно подметил товарищ Etki, есть битовая операция... Приводим float к int (отбрасываем всё, что после запятой). Побитово проходим слева направо (с первого или второго бита в зависимости от unsigned или signed) и ищем первый бит с единицей. После этого бита зануляем все оставшиеся биты, но если это был последний бит, то "число"==1 (что с ним делать, вам виднее). в данном случае нужен не "цикл", а набор констант и if'ов. можно сделать и циклом: Делаем массив из Х элементов (степени двоек, Х равен количеству битов у переменной). Сдвигаем вправо и увеличиваем счётчик. Результат равен нулю? Если да, то в счётчике хранится положение последней единицы (если считать справа налево), иначе повторяем цикл. Берём результат из массива по массив[счётчик]. По скорости работы сложно сказать, но, вероятно, второй способ быстрее (если учесть, что компилятор оптимизирует), но всё равно лучше затестить.

Ответ 4



Проще всего, по-моему, будет сделать таблицу степеней двойки, считать ее один раз и в рантайме поэлементно сравнивать А со значениями таблицы. Можно чуть-чуть ускорить процесс, предполагая ближайшее значение с помощью пары условных операторов. Наверняка есть крышесносящее битовое решение, но я пока не вижу четкой схемы решения в голове.

Ответ 5



function show_str4($str){ return sprintf("%b %b %b %b",ord($str[0]),ord($str[1]),ord($str[2]),ord($str[3])); } function get_mask(){ $c00=chr(0); $cff=chr(255); $test = pack("f",5e-1); $m = pack("f",25e-2); $mask = $c00.$c00.$c00.$cff | $m; if (($mask & $test) == $test) return $mask; $mask = $c00.$c00.$cff.$c00 | $m; if (($mask & $test) == $test) return $mask; $mask = $c00.$cff.$c00.$c00 | $m; if (($mask & $test) == $test) return $mask; $mask = $cff.$c00.$c00.$c00 | $m; if (($mask & $test) == $test) return $mask; exit(1); } function floattoexp2($x){ $arr=unpack("f",pack("f",$x) & get_mask()); return $arr[1]; } $x = (float)345.67; $y=floattoexp2($x); $x_packed = pack("f",$x); $mask = get_mask(); $y_packed = $x_packed & $mask; printf("x=$x y=$y
x_packed=".show_str4($x_packed)."
mask=".show_str4($mask)."
y_packed=".show_str4($y_packed)); Результаты: x=345.67 y=256 x_packed=11000011 11010101 10101100 1000011 mask=0 0 10000000 11111111 y_packed=0 0 10000000 1000011 Суть алгоритма - в обработке внутреннего представления float-числа по маске. Маска формируется так: Положение байта с порядком имеет 4 варианта, проблема решена перебором. При этом местоположение мантиссы определяется автоматически, с использованием константы 0.25. Алгоритм не может быть рекомендован для кроссплатформенных приложений.

Ответ 6



В нормализованном внутреннем битовом представлении заданного числа обнулить все разряды мантиссы, кроме первого. Если порядок пишется в допкоде, то для этого достаточно логически умножить внутреннее представление числа на внутреннее представление константы 0.5 или 0.25, в зависимости от способа представления числа. Минус предложения - отсутствие универсальности.

Ответ 7



public class PositionInsertToList { public static int searchPosition(long arr[], long key){ int l = -1; int r = arr.length; while(l != r - 1){ int mid = (l + r ) >> 1; if(key < arr[mid]) r = mid; else l = mid; } return r; } public static void main(String[] args) { long a[] = {2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536}; System.out.println(searchPosition(a, (long)345.53)); } } Применяем бинарный поиск. Работает за log(a.length) - двоичный логарифм. Если в массиве 32 элемента, то поиск осуществляется за 6 действий. Получаем индекс, куда бы мы вставили наше число. В данном случае это число 8. Получается, что в этой позиции число больше нашего, а в позиции 7 число меньше нашего.

пятница, 20 декабря 2019 г.

Побитовая операция

#java #битовые_операции


Что происходит в данной строке?

((in.read() & 0xFF) << 8 | (in.read() & 0xFF)) //


В первый in.read() поступает 78, во-второй 132

Насколько я понимаю, смещение, но можно по-подробнее? Ну или же где про это почитать?
    


Ответы

Ответ 1



Происходит чтение чего-то - A= 0x......ab (байты A, количество разрядов зависит от определения функции in.read()) Выделяется младший байт A и смещается влево (во второй байт) 0x00ab00 Происходит новое чтение C= 0x......cd (байты C) Выделяется младший байт C и объединяется c предыдущим результатом. Получается 0x00abcd Это целочисленное значение, младшее слово которого содержит оба результата чтения. Для приведённого примера чтения 78 и 132 получится 20100 (78*256+132)

Ответ 2



В данной строке из потока считывается двухбайтовое число, представленное в формате big endian («старшим байтом вперёд»). Дело в том, что многобайтовые числа можно представить двумя способами: Старшим байтом вперёд (big endian, он же сетевой порядок байтов). При нём 0xabcd хранится как AB CD. Этот способ традиционно используют при передаче данных через интернет. Младшим байтом вперёд (little endian, он же Intel-овский порядок байтов). При нём 0xabcd хранится уже как CD AB. Этот способ является родным для большого количества процессоров (включая x86-совместимые, один из которых находится в вашем компьютере). Расположение числа в памяти при различных порядках байтов. Источник: Википедия, #1 , #2 Могу предположить, что in — это сокет, и используемый вами прикладной протокол предписывает использовать big-endian. Как результат, при чтении многобайтных чисел напрямую получится мусор из-за несовпадения используемого и родного порядков байт. Соответственно, необходимо выполнить преобразование одним из двух способов: Прочитать число целиком и переставить байты на месте; Прочитать число побайтово, сразу же размещая очередной байт на подобающее ему место. В вашем случае используется второй подход. Замечу, что оба подхода сработают вне зависимости от родного порядка байт платформы, так как не важно, как будет храниться целевое число — важно, в какие разряды универсального (где старшие разряды всегда слева — 0xabcdefgh...) представления вы их запишете — остальным озаботится процессор.

четверг, 19 декабря 2019 г.

Как заполнить область в байте?

#cpp #c #алгоритм #битовые_операции


Допустим, есть байт 0b00001000, для удобства разделю: 0 b 0000 - 1000

Как заполнить область в байте своим числом? 

Вот пример, с 1 по 4 бит надо записать 0011, выйдет:


  0 b 0000 - 0110


Или с 0 по 1 надо записать 11, выйдет:


  0 b 0000 - 1011

    


Ответы

Ответ 1



#include #include int main() { unsigned int from = 3; // начиная с 4-го бита unsigned int to = 6; // по 6 бит unsigned int val = 11; // записать это значение const int BIT_COUNT = sizeof(unsigned int) * CHAR_BIT; std::bitset x(555); // исходное число std::bitset v(val); std::bitset m(0); // битовая маска std::cout << "x = " << x << std::endl; std::cout << "value = " << v << std::endl; m.flip(); m <<= to - from + 1; m.flip(); m <<= from; m.flip(); std::cout << "mask = " << m << std::endl; x = x & m; v <<= from; x = x | v; std::cout << "res = " << x << std::endl; return 0; }

Ответ 2



Делаем маску на нужные биты (например для 2345 битов - 11100001) Обнуляем старые биты используя AND по маске, получим 0 на их месте Подготавливаем новые биты (используем смещение вверх для позиционирования) Записываем новые биты используя OR

Ответ 3



Например, можно сделать следующим образом: #include #include #include int main() { std::uint8_t b = 0b00001000; std::uint8_t b1 = ( ( ~0u << 4 ) & b ) | 0b0011; std::uint8_t b2 = ( ( ~0u << 2 ) & b ) | 0b0011; std::cout << "b = " << std::hex << ( int )b << std::endl; std::cout << "b1 = " << std::hex << ( int )b1 << std::endl; std::cout << "b2 = " << std::hex << ( int )b2 << std::endl; return 0; } Вывод на консоль: b = 8 b1 = 3 b2 = b Можете написать отдельную функцию, как, например, std::uint8_t replace( std::uint8_t src, std::uint8_t value, size_t bits ) { return ( ( ~0u << bits ) & src ) | ( ~( ~0u << bits ) & value ); } Вот программа с использованием функции #include #include #include std::uint8_t replace( std::uint8_t src, std::uint8_t value, size_t bits ) { return ( ( ~0u << bits ) & src ) | ( ~( ~0u << bits ) & value ); } int main() { std::uint8_t b = 0b00001000; std::uint8_t b1 = replace( b, 0b0011, 4 ); std::uint8_t b2 = replace( b, 0b0011, 2 ); std::cout << "b = " << std::hex << ( int )b << std::endl; std::cout << "b1 = " << std::hex << ( int )b1 << std::endl; std::cout << "b2 = " << std::hex << ( int )b2 << std::endl; return 0; } Вывод на консоль точно такой же, как показано выше: b = 8 b1 = 3 b2 = b Если нужно задавать позицию, то функция может выглядеть следующим образом std::uint8_t replace( std::uint8_t src, std::uint8_t value, size_t n, size_t pos ) { if ( n == 0 ) return src; return ( ( ~( ( 1u << n ) - 1) << pos ) & src ) | ( ~( ~0u << n ) & value ); }

Ответ 4



Используйте битовую арифметику. Пусть есть две переменные unsigned char (по 8 бит в каждой, соответственно). | — побитовое 'или': 0b11 | 0b101 = 0b111 & — побитовое 'и': 0b11 & 0b101 = 0b1 ~ — побитовое 'не': ~0b11 = 0b11111100 << — сдвиг влево: 0b101 << 2 = 0b10100 >> — сдвиг вправо: 0b1010 >> 2 = 0b10 Теперь можно манипулировать битами числа. К примеру, для вашего случая: unsigned char x = 0b00001000 x &= 0b11100001 // очистили место для вставки // x = 0b00001000 & 0b11100001 = 0b00000000 x += (0b0011 << 1) // x = 0b00000000 + (0b0011 << 1) = // = 0b00000000 + 0b00110 = // = 0b00000110

Ответ 5



Можно предложить несложную реализацию с побитовой обработкой вставки. function masking($number,$mask,$start,$finish){ $result=$number; for($i=$start; $i<=$finish; $i++){ $bit=1<<$i; $result = ($result|$bit) - $bit + ($bit & $mask); } return $result; } $number=0b00001000; $mask=0b00000110; $number_masked = masking($number,$mask,1,4); printf("number=%b mask=%b start=%2d finish=%2d result=%b
", $number, $mask, 1, 4, $number_masked); $mask=0b00000011; $number_masked = masking($number,$mask,0,1); printf("number=%b mask=%b start=%2d finish=%2d result=%b
", $number, $mask, 0, 1, $number_masked); Результаты: number=1000 mask=110 start= 1 finish= 4 result=110 number=1000 mask=11 start= 0 finish= 1 result=1011

Ответ 6



Если можно избежать скучного вычисления битов, избегайте его. Пусть за вас считает компилятор. Воспользуйтесь именоваными структурами и битовыми полями. (Да и наверняка структуры данных наподобие тех, которые я привёл, найдутся в документации.) enum class PCLK_root_divider : unsigned char { pll_clki_1 = 0, pll_clki_2 = 1, pll_clki_4 = 2, pll_clki_8 = 3 }; enum class sclk2x_root_divider : unsigned char { pll_clki_1 = 0, pll_clki_2 = 1, pll_clki_4 = 2, pll_clki_8 = 3 }; enum class SCLK_root_divider : unsigned char { pll_clki_1 = 0, pll_clki_2 = 1, pll_clki_4 = 2, pll_clki_8 = 3 }; struct Whatever { SCLK_root_divider _SCLK_root_divider : 2; sclk2x_root_divider _sclk2x_root_divider : 2; PCLK_root_divider _PCLK_root_divider : 2; unsigned char _debug_mode : 2; }; Только дайте полям какие-то более подходящие имена. Важно: не забывайте про big/little endian! Порядок полей может быть противоположным на другой архитектуре. Проверка: http://ideone.com/RfRrk2

Ответ 7



Если отметить область вставки единичными битами шаблона ($template), а саму вставку задать в виде байта ($patch), в котором "свои" биты находятся на требуемых позициях, то результат получается в одну строчку: function patching($number, $patch, $template){ return $number & (255-$template) | $patch & $template; } printf("number=%08b patch=%08b template=%08b result=%08b
", $number=0b00001000, $patch=0b00000110, $template=0b00011110, patching($number, $patch, $template)); printf("number=%08b patch=%08b template=%08b result=%08b
", $number=0b00001000, $patch=0b00000011, $template=0b0000011, patching($number, $patch, $template)); Результаты: number=00001000 patch=00000110 template=00011110 result=00000110 number=00001000 patch=00000011 template=00000011 result=00001011 При этом гибкость процедуры повышается, поскольку вставляемые байты не обязаны находиться рядом.