Немного поискав решение данной проблемы в интернете, я пришел к выводу, что быстрее будет самому решить данную проблему.
Отмечу, что также была необходимость смотреть и изменять значения отдельных битов (это можно сделать с помощью битовых флагов или класса BitArray, но я решил действовать по-другому), еще имелась необходимость реверсировать порядок битов.
Итак, имея немного опыта программирования за плечами, я решил реализовать все описанные выше функции в статическом классе.
Для просмотра и изменения значения отдельных битов, я реализовал 2 статических метода GetBitи SetBit, первый в качестве параметров принимает байт у которого необходимо посмотреть значение бита и, собственно, сам номер бита, во втором добавляется еще и значение устанавливаемого бита в виде булевской переменной. Реализация методов достаточно тривиальна:
///<summary> /// Возвращает бит в num байте val ///</summary> ///<param name="val">Входнойбайт</param> ///<param name="num">Номербита, начинаяс 0</param> ///<returns>true-битравен 1, false- битравен 0</returns> public static bool GetBit(byte val,int num) { if ((num> 7) || (num< 0))//Проверка входных данных { throw new ArgumentException(); } return ((val>>num)&1)>0;//собственно все вычисления } ///<summary> /// Устанавливает значение определенного бита в байте ///</summary> ///<param name="val">Входнойбайт</param> ///<param name="num">Номербита</param> ///<param name="bit">Значениебита: true-битравен 1, false- битравен 0 </param> ///<returns>Байт, с измененным значением бита</returns> public static byte SetBit(byte val, int num,bool bit) { if ((num> 7) || (num< 0))//Проверка входных данных { throw new ArgumentException(); } byte tmpval = 1; tmpval = (byte)(tmpval<<num);//устанавливаем нужный бит в единицу val = (byte)(val& (~tmpval));//сбрасываем в 0 нужный бит if (bit)// если бит требуется установить в 1 { val = (byte)(val | (tmpval));//то устанавливаем нужный бит в 1 } return val; }
Не заморачиваясь поиском модных и быстрых алгоритмов по реверсу битов, сделал следующее:
///<summary> /// Изменяет порядок битов на обратный ///</summary> ///<param name="val">Входнойбайт</param> ///<returns>Байт с обратным порядком битов</returns> Public static byte Reverse(byte val) { int i = 0; byterez = 0; for (i = 0; i < 8; i++) { rez = (byte)(rez<< 1); if (((val>> i) & 1) > 0) { rez = (byte)(rez | 1); } } return rez; }
Но самое интересное было приведение чисел произвольной разрядности к виду понятному машине, в случае если бы они все были беззнаковыми, проблем бы не было- считал нужное количество байтов, преобразовал в нужный формат и отсек ненужные биты, но так как встречались как знаковые так и беззнаковые числа пришлось немного выкручиваться, т.к. знаковый бит не будет понят как знаковый и числа сами по себе хранятся в дополнительном коде:
///<summary> /// Преобразует массив байтов в Int64, начиная с бита firstbit и заканчивая битом lastbit, с учетом знаковости числа ///</summary> ///<paramname="bytes">Массив байтов, начиная со старшей части числа</param> ///<param name="firstbit">Младшийбитчисла</param> ///<param name="lastbit">Старшийбитчисла</param> ///<paramname="issigned">Показывает со знаком ли число</param> ///<returns>Результат преобразования Int64</returns> public static Int64 HandleBytes(byte[] bytes, int firstbit, int lastbit,bool issigned) { if ((bytes == null) || (firstbit< 0) || (firstbit> 63) || (lastbit< 0) || (lastbit> 63) || (((lastbit - firstbit) == 63) && (!issigned)) ||//если число без знака и при этом 64-разрядное, то такое число физически нельзя вернуть в Int64 (bytes.Length> 8) || (bytes.Length< 1)|| (firstbit>lastbit)) { throw new ArgumentException(); } Int64 rezult = 0,//переменная для результата tmp = 0;//переменная для промежуточных вычислений //Заполняем переменную результата входным массивом байтов foreach (byte tmp1 in bytes) { rezult = rezult<< 8; rezult = rezult | tmp1; } rezult = rezult>>firstbit;//отсекаем ненужные младшиебиты if (firstbit != lastbit) { tmp = (long)(Math.Pow(2, lastbit - firstbit));//1 в знаковом бите, остальные 0 if ((issigned)&&((rezult&tmp)>0))//если число знаковое и отрицательно { //это все необходимо потому что числа сами по себе записываются в дополнительном коде tmp = tmp - 1;//все 1 начиная с младшего бита и до знакового бита, не включая знаковый бит rezult = ~rezult;// инвертируем биты rezult = rezult&tmp;// отсекам старшие ненужные биты rezult++; rezult *=-1; } else// если число беззнаковое или знаковое, но имеет положительное значение { tmp = tmp + (tmp - 1);//все 1 начиная с младшего бита и до знакового бита, включая знаковый бит rezult = rezult&tmp;//отсекаем ненужные биты } } else//Если младший и старший биты равны, то нужно просто вернуть один бит { rezult = rezult& 1; } return rezult; }
В качестве параметров метод принимает массив байтов, количество байт от 1 до 8, старшая часть в 0. firstbit –номер младшего бита числа, lastbit –номер старшего бита числа, issigned- указывает знаковое число или беззнаковое. Таким образом если число знаковое и располагается в разрядах 3-18, то вызов метода будет выглядеть HandleBytes(bytes, 3, 18,true).
Первые 3 метода в качестве входных параметров имеют байт, естественно, по необходимости можно легко сделать перегруженные версии для других типов параметров.
Надеюсь хоть кому-то это будет хоть немного полезно.
ссылка на оригинал статьи http://habrahabr.ru/post/156021/
Добавить комментарий