Биты, байты. Битовые операции. Числовые типы данных. Бит — это базовая единица измерения количества информации, равная количеству информации, содержащемуся в опыте, имеющем два равновероятных исхода; см. информационная энтропия. Это тождественно количеству информации в ответе на вопрос, допускающий ответ «да» или «нет» и никакого другого (то есть такое количество информации, которое позволяет однозначно ответить на поставленный вопрос). Байт состоит из восьми элементарных разрядов — битов, в каждом из которых может храниться одно значение: либо 0, либо 1. Поскольку в байте 8 разрядов, то одним байтом можно выразить 2 в восьмой степени = 256 разных значений (от 0 до 255). Побитовые операции (англ. bitwise operations) — операции, производимые над цепочками битов. Выделяют два типа побитовых операций: логические операции и побитовые сдвиги. Логические побитовые операции Битовые операторы И ИЛИ , ИЛИ , НЕ и исключающее используют те же таблицы истинности, что и их логические эквиваленты. Побитовое И Побитовое И используется для выключения битов. Любой бит, установленный в , вызывает установку соответствующего бита результата также в . & 11001010 11100010 11000010 Побитовое ИЛИ Побитовое ИЛИ используется для включения битов. Любой бит, установленный в , вызывает установку соответствующего бита результата также в . | 11001010 11100010 11101010 Побитовое НЕ Побитовое НЕ инвертирует состояние каждого бита исходной переменной. ~ 11001010 00110101 Побитовое исключающее ИЛИ Исключающее ИЛИ устанавливает значение бита результата в , если значения в соответствующих битах исходных переменных различны. ^ 11001010 11100010 00101000 Побитовые сдвиги Операторы сдвига и сдвигают биты в переменной влево или вправо на указанное число. При этом на освободившиеся позиции устанавливаются нули (кроме сдвига вправо отрицательного числа, в этом случае на свободные позиции устанавливаются единицы, так как числа представляются в двоичном дополнительном коде и необходимо поддерживать знаковый бит). Сдвиг влево может применяться для умножения числа на два, сдвиг вправо — для деления. x = 7 // 00000111 (7) x = x >> 1 // 00000011 (3) x = x << 1 // 00000110 (6) x = x << 5 // 11000000 (-64) x = x >> 2 // 11110000 (-16) Оператор Действие & И | ИЛИ ^ Исключающее ИЛИ ~ Дополнение >> Сдвиг вправо Оператор Действие << Сдвиг влево Битовые операторы И, ИЛИ, НЕ используют ту же таблицу истинности, что и их логические эквиваленты, за тем исключением, что они работают побитно. Исключающее ИЛИ имеет следующую таблицу истинности: р q p^q 0 0 0 0 1 1 1 0 1 1 1 0 Как следует из таблицы, исключающее ИЛИ выдает истину, если только один из операндов истинен. В противном случае получается ложь. Битовые операторы наиболее часто применяются при разработке драйверов устройств, например программ для модемов, дисков и принтеров, поскольку битовые операторы могут использоваться для выключения некоторых битов, например четности. (Бит четности используется для подтверждения того, что остальные биты в байте не изменялись. Он, как правило, является старшим битом в байте.) Битовое И чаще всего используется для выключения битов То есть любой бит, установленный в 0, вызывает установку соответствующего бита в Другом операнде также в 0. Например, следующая функция читает символы из порта модема, используя функцию read_modem(), и сбрасывает бит четности в 0. char get_char_from_modem(void) { char ch; ch = read_modem (); /* получение символа из порта модема * / return (ch & 127); } Четность отображается восьмым битом, который устанавливается в 0 с помощью битового И, поскольку биты с номерами от 1 до 7 установлены в 1, а бит с номером 8 — в 0. Выражение ch & 127 означает, что выполняется битовая операция И между битами переменной ch и битами числа 127. В результате получим ch со сброшенным старшим битом. В следующем примере предполагается, что ch имеет символ 'А' и имеет бит четности: бит четности | & 11000001 ch содержит 'А' с битом четности 01111111 127 в двоичном представлении выполнение битового И --------------01000001 'А' без бита четности Битовое ИЛИ может использоваться для установки битов. Любой бит, установленный в любом операнде, вызывает установку соответствующего бита в другом операнде. Например, в результате операции 128 | 3 получаем | 10000000 128 в двоичном представлении 00000011 3 в двоичном представлении -------------- битовое ИЛИ 10000011 результат Исключающее ИЛИ или как его называют, XOR устанавливает бит, если соответствующие биты в операндах отличаются. Например, в результате операции 127 ^ 120 получаем ^ 01111111 127 в двоичном представлении 01111000 120 к двоичном представлении битовое исключающее -------------- ИЛИ 00000111 результат В общем, битовые И, ИЛИ и исключающее ИЛИ применяются к каждому биту переменной. Поэтому битовые операторы обычно не используются в условных операторах, которыми являются операторы отношения и логические операторы. Например: если х содержит 7, то х && 8 выдаст 1, в то время как х & 8 выдаст 0. Операторы сдвига >> и << сдвигают биты в переменной вправо и влево на указанное число. Общий вид оператора сдвига вправо: переменная >> число сдвигов а общий вид оператора сдвига влево: переменная << число сдвигов Помните, что сдвиг — это не то же самое, что и вращение, то есть биты, сдвигающиеся на один конец, не появляются с другого. Сдвинутые биты теряются, а с другого конца появляются нули. В том случае, если вправо сдвигается отрицательное число, слева появляются единицы (поддерживается знаковый бит). Операции битового сдвига могут быть полезны при декодировании информации от внешних устройств и для чтения информации о статусе. Операторы битового сдвига могут также использоваться для выполнения быстрого умножения и деления целых чисел. Сдвиг влево равносилен умножению на 2, а сдвиг вправо - делению на 2, как показано в таблице. Битовое представление х после выполнения каждого оператора Значение х 00000111 00001110 01110000 11000000 01100000 00011000 7 14 112 192 96 24 char х; x = 7; x = x << 1; x = x << 3; x = x << 2; х = х >> 1; x = x >> 2; Каждый сдвиг влево приводит к умножению на 2. Обратим внимание, что после сдвига х << 2 информация теряется, п за конец байта. Каждый сдвиг вправо приводит к делению на 2. Обратим внимание, что деление не вернуло потерянные биты. Оператор дополнение, ~, инвертирует состояние каждого бита указанной переменной, то есть 1 устанавливается в 0, а 0 — в 1. Битовые операторы часто используются в процедурах шифрования. Если есть желание сделать дисковый файл нечитабельным, можно выполнить над ним битовую операцию. Одним из простейших методов является использование битового дополнения для инверсии каждого бита в байте, как показано ниже: Исходный байт После первого дополнения После второго дополнения Надо обратить внимание, что в результате выполнения двух битовых дополнений получаем исходное число. Следовательно, первое дополнение будет создавать кодированную версию байта, а второе будет декодировать. 00101100 11010011 00101100 Можно использовать показанную ниже функцию encode() для кодирования символа: /* Простейшая шифрующая функция */ char encode(code ch) { return(~ch); /* дополнение */ } Типы данных имеют особенное значение в C#, поскольку это строго типизированный язык. Это означает, что все операции подвергаются строгому контролю со стороны компилятора на соответствие типов, причем недопустимые операции не компилируются. Следовательно, строгий контроль типов позволяет исключить ошибки и повысить надежность программ. Для обеспечения контроля типов все переменные, выражения и значения должны принадлежать к определенному типу. Такого понятия, как "бестиповая" переменная, в данном языке программирования вообще не существует. Более того, тип значения определяет те операции, которые разрешается выполнять над ним. Операция, разрешенная для одного типа данных, может оказаться недопустимой для другого. В C# имеются две общие категории встроенных типов данных: типы значений и ссылочные типы. Они отличаются по содержимому переменной. Концептуально разница между ними состоит в том, что тип значения (value type) хранит данные непосредственно, в то время как ссылочный тип (reference type) хранит ссылку на значение. Эти типы сохраняются в разных местах памяти: типы значений сохраняются в области, известной как стек, а ссылочные типы — в области, называемой управляемой кучей. Целочисленные типы В C# определены девять целочисленных типов: char, byte, sbyte, short, ushort, int, uint, long и ulong. Но тип char применяется, главным образом, для представления символов и поэтому рассматривается отдельно. Остальные восемь целочисленных типов предназначены для числовых расчетов. Ниже представлены их диапазон представления чисел и разрядность в битах: Целочисленные типы C# Тип Тип CTS Разрядность в битах Диапазон byte System.Byte 8 0:255 sbyte System.SByte 8 -128:127 short System.Int16 16 -32768 : 32767 ushort System.UInt16 16 0 : 65535 int System.Int32 32 -2147483648 : 2147483647 uint System.UInt32 32 0 : 4294967295 long System.Int64 64 -9223372036854775808 : 9223372036854775807 ulong System.UInt64 64 0 : 18446744073709551615 Как следует из приведенной выше таблицы, в C# определены оба варианта различных целочисленных типов: со знаком и без знака. Целочисленные типы со знаком отличаются от аналогичных типов без знака способом интерпретации старшего разряда целого числа. Так, если в программе указано целочисленное значение со знаком, то компилятор C# сгенерирует код, в котором старший разряд целого числа используется в качестве флага знака. Число считается положительным, если флаг знака равен 0, и отрицательным, если он равен 1. Отрицательные числа практически всегда представляются методом дополнения до двух, в соответствии с которым все двоичные разряды отрицательного числа сначала инвертируются, а затем к этому числу добавляется 1. Вероятно, самым распространенным в программировании целочисленным типом является тип int. Переменные типа int нередко используются для управления циклами, индексирования массивов и математических расчетов общего назначения. Когда же требуется целочисленное значение с большим диапазоном представления чисел, чем у типа int, то для этой цели имеется целый ряд других целочисленных типов. Так, если значение нужно сохранить без знака, то для него можно выбрать тип uint, для больших значений со знаком — тип long, а для больших значений без знака — тип ulong. В качестве примера ниже приведена программа, вычисляющая расстояние от Земли до Солнца в сантиметрах. Для хранения столь большого значения в ней используется переменная типа long: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { long result; const long km = 149800000; // расстояние в км. result = km * 1000 * 100; Console.WriteLine(result); Console.ReadLine(); } } } Всем целочисленным переменным значения могут присваиваться в десятичной или шестнадцатеричной системе обозначений. В последнем случае требуется префикс 0x: long x = 0x12ab; Если возникает какая-то неопределенность относительно того, имеет ли целое значение тип int, uint, long или ulong, то по умолчанию принимается int. Чтобы явно специфицировать, какой другой целочисленный тип должно иметь значение, к числу можно добавлять следующие символы: uint ui = 1234U; long l = 1234L; ulong ul = 1234UL; U и L можно также указывать в нижнем регистре, хотя строчную L легко зрительно спутать с цифрой 1 (единица). Типы с плавающей точкой Типы с плавающей точкой позволяют представлять числа с дробной частью. В C# имеются две разновидности типов данных с плавающей точкой: float и double. Они представляют числовые значения с одинарной и двойной точностью соответственно. Так, разрядность типа float составляет 32 бита, что приближенно соответствует диапазону представления чисел от 5E-45 до 3,4E+38. А разрядность типа double составляет 64 бита, что приближенно соответствует диапазону представления чисел от 5E-324 до 1,7Е+308. Тип данных float предназначен для меньших значений с плавающей точкой, для которых требуется меньшая точность. Тип данных double больше, чем float, и предлагает более высокую степень точности (15 разрядов). Если нецелочисленное значение жестко кодируется в исходном тексте (например, 12.3), то обычно компилятор предполагает, что подразумевается значение типа double. Если значение необходимо специфицировать как float, потребуется добавить к нему символ F (или f): float f = 12.3F; Десятичный тип данных Для представления чисел с плавающей точкой высокой точности предусмотрен также десятичный тип decimal, который предназначен для применения в финансовых расчетах. Этот тип имеет разрядность 128 бит для представления числовых значений в пределах от 1Е-28 до 7,9Е+28. Вам, вероятно, известно, что для обычных арифметических вычислений с плавающей точкой характерны ошибки округления десятичных значений. Эти ошибки исключаются при использовании типа decimal, который позволяет представить числа с точностью до 28 (а иногда и 29) десятичных разрядов. Благодаря тому что этот тип данных способен представлять десятичные значения без ошибок округления, он особенно удобен для расчетов, связанных с финансами: using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { // *** Расчет стоимости капиталовложения с *** // *** фиксированной нормой прибыли*** decimal money, percent; int i; const byte years = 15; money = 1000.0m; percent = 0.045m; for (i = 1; i <= years; i++) { money *= 1 + percent; } Console.WriteLine("Общий доход за {0} лет: {1} $$",years,money); Console.ReadLine(); } } }