PureBasic

Работа с разными системами счисления

Примечание: Эти примеры используют символ ^ в значении 'возвести в степень' - это лишь условность этого документа, PureBasic на данный момент не имеет операции возведения в степень! Вместо неё используйте команду Pow() из библиотеки "Math".

Введение

Система счисления это способ представления чисел, используя определённое количество возможных символов на цифру (разряд). Наиболее вам знакома система с основанием 10 (десятичная), потому что цифр существует 10 (от 0 до 9) и она наиболее проста в обращении для большинства людей.

Цель этой страницы объяснить разные системы счисления и как с ними работать. Причина этого в том, что компьютеры работают в двоичной системе (с основанием 2) и в некоторых случаях это полезно понимать (например при использовании логических операций, битовых масок и т.д.).

Обзор систем счисления

Десятичная система

Представьте себе десятичное число, а затем представьте, что оно разделено на колонки. Возьмём для примера число 1234. Разделение на колонки даёт нам:
  1   2   3   4
Теперь представьте, какой заголовок у каждой колонки. Мы знаем, что это единицы, десятки, сотни и тысячи, расположенные вот так:
  1000  100   10    1
     1    2    3    4
Видно, что число 1234 составляется из
    1*1000=1000
  + 2* 100= 200
  + 3*  10=  30
  + 4*   1=   4
  Total   =1234
Если посмотреть на заголовки колонок, вы увидите, что каждый раз, когда вы передвигаетесь на одну колонку влево, мы умножаем на 10, что оказывается основанием системы счисления. Каждый раз, когда вы передвигаетесь на одну колонку вправо, вы делите на 10. Заголовки колонок можно назвать весами, так как для получения числа нам нужно умножить цифры в каждой колонке на вес. Мы можем выразить веса через индексы. Например 10^2 означает '10 в степени два' или 1*10*10 (=100). Аналогично 10^4 означает 1*10*10*10*10 (=10000). Заметьте закономерность, каково значение индекса, столько раз мы умножаем возводимое в степень число. 10^0 означает 1 (так как мы умножаем на 10 ноль раз). Использование отрицательных чисел показывает, что нам нужно делить, например 10^-2 означает 1/10/10 (=0.01). Значения индексов приобретают более очевидный смысл, если мы дадим каждой колонке номер - вы часто будете встречать вещи вроде 'бит 0' что означает 'двоичная цифра в колонке 0'.
  В этом примере, ^ означает, возведение в степень, таким образом, 10^2 означает 10 возведение в степень из 2.
  Номер разряда               3     2     1     0
  Вес   (Индекс)            10^3  10^2  10^1  10^0
  Вес(фактическое значение) 1000   100    10     1
  Пример числа (1234)         1     2     3     4
Несколькими абзацами ранее мы показали как преобразовать число 1234 в его десятичное представление. Довольно бессмысленно, так как оно уже в десятичной системе, но так можно показать общий метод - вот как преобразовать любое число в его десятичное значение:
  B = Значение основания системы счисления
  
  1) Разделите число в любой системе счисления на колонки. Например,
     если у нас значение 'abcde' по вымышленному основанию 'B',
     колонки будут:   a   b   c   d   e
  
  2) Умножьте каждый символ на вес этой колонки (вес вычисляется
     как 'B' возведённое в степень номера колонки):
       a * B^4 = a * B * B * B * B
       b * B^3 = b * B * B * B
       c * B^2 = c * B * B
       d * B^1 = d * B
       e * B^0 = e
  
  3) Вычислите сумму всех этих значений. Если в ходе вычислений записывать 
     все эти значения в десятичной форме, станет намного проще понять 
     результат и делать вычисления (если мы преобразовываем десятичную систему).
Преобразование в противоположном направлении (из десятичной системы в систему с основанием 'B') делается с использованием деления вместо умножения:
  1) Начните с десятичного числа, которое вы хотите преобразовать (например 1234).
  
  2) Разделите на требуемое основание ('B') и запомните результат и 
     остаток.
  
  3) Разделите результат (2) на требуемое основание ('B') и запомните
     результат и остаток.
  
  4) Продолжайте так делить, пока не получите результат 0.
  
  5) Ваше число в требуемой системе счисления это остатки, записанные в порядке
     от последнего вычисленного к первому. Например ваше число составляется
     из остатков шагов в порядке 432.

Более конкретные примеры даны в параграфах о конкретных системах счисления.

Двоичная система

Всё в компьютере хранится в двоичной системе (основание 2, дающее символы '0' или '1') но работа с двоичными числами следует тем же правилам, что и с десятичными. Каждый символ двоичного числа называется бит (bit), сокращение от binary digit. Обычно вы работаете с байтами (8 бит), словами (16 бит) или длинными словами (32 бита) так как это размеры по умолчанию встроенных типов PureBasic. Веса для байта:
  (^ означает 'возвести в степень', основание системы 2 для двоичной)
  Номер бита/колонки         7     6     5     4     3     2     1     0
  Вес (индекс)             2^7   2^6   2^5   2^4   2^3   2^2   2^1   2^0
  Вес (значение)           128    64    32    16     8     4     2     1
Например, если у нас число 00110011 (Основание 2), мы можем найти его значение так:
    0 * 128
  + 0 *  64
  + 1 *  32
  + 1 *  16
  + 0 *   8
  + 0 *   4
  + 1 *   2
  + 1 *   1
  =      51
Примером обратного преобразования была бы запись значения 69 в двоичной системе. Мы сделаем это так:
  69 / 2 = 34 r 1     ^
  34 / 2 = 17 r 0    /|\
  17 / 2 =  8 r 1     |
   8 / 2 =  4 r 0     |    Читаем остатки в этом направлении
   4 / 2 =  2 r 0     |
   2 / 2 =  1 r 0     |
   1 / 2 =  0 r 1     |
  (Здесь стоп, так как результат последнего деления был 0)

  Читаем остатки назад для получения двоичного значения = 1000101
Ещё что следует учесть при работе с двоичными числами, это представление отрицательных чисел. В повседневном использовании, мы просто помещаем символ отрицательного числа впереди десятичного числа. Мы не может так сделать в двоичной системе, но способ есть (в конце концов PureBasic работает в основном со знаковыми числами, поэтому должен уметь управляться с отрицательными числами). Этот метод называется 'дополнение до двух' и помимо всех его хороших черт (здесь не объясняемых для уменьшения путаницы) простейший способ думать о нём - считать, что вес старшего бита (бит с наибольшим весом, в случае байта это бит 7) это отрицательное значение. Так что для системы дополнения до двух веса битов меняются на:
  (^ означает 'возвести в степень', основание системы 2 для двоичной)

  Номер бита/колонки            7     6     5     4     3     2     1     0
  Вес (индекс)                -2^7   2^6   2^5   2^4   2^3   2^2   2^1   2^0
  Вес (значение)              -128    64    32    16     8     4     2     1
и вы делаете преобразование из двоичной системы в десятичную точно так же, как выше, но используя новый набор весов. Например, число 10000000 (Основание 2) это -128, а 10101010 (Основание 2) это -86.

Преобразование положительного двоичного числа в отрицательное двоичное число, происходит в два этапа. Сначала содержимое каждого разряда инвертируется ( 0 меняется на 1, а 1 на 0). Затем к полученному результату прибавляется единица. Например, для отрицания числа 00100010 происходит инверсия-> 11011101 и прибавляется 1 -> 11011110.

Это делает преобразование из десятичной системы в двоичную проще, так как вы можете преобразовывать отрицательные десятичные числа как их положительные эквиваленты (используя показанный выше метод) и затем делать двоичное число отрицательным в конце.

Двоичные числа записываются в PureBasic с символом процента перед ними, и очевидно все биты числа должны быть '0' или '1'. Например, вы можете использовать значение %110011 в PureBasic для обозначения 51. Заметьте, что вам не нужно добавлять ведущие '0' (это число на самом деле равно %00110011), но если вы укажете полное количество битов, это может помочь удобочитаемости ваших исходников.

Шестнадцатеричная Система

Шестнадцатеричная система (основание 16, символы '0'-'9' и 'A'-'F') это система счисления, которая наиболее широко используется при работе с компьютерами, так как это наверно самая простая для человеческого понимания из недесятичных систем, и вам не приходится иметь дело с длинными строками символов в качестве чисел (как в случае работы с двоичной системой).

Шестнадцатеричная математика следует тем же правилам, что и десятичная, хотя у вас теперь есть 16 символов на колонку пока вам не понадобится перенос/заём. Преобразование между шестнадцатеричной и десятичной системами следует тем же закономерностям, как между двоичной и десятичной, кроме того что веса теперь умножаются на 16 и делятся на 16 вместо 2:
  Номер разряда                  3      2      1      0
  Вес (индекс)                 16^3   16^2   16^1   16^0
  Вес (значение)               4096    256     16      1
Преобразование шестнадцатеричного значения BEEF (Основание 16) в десятичное делается так:
    B * 4096 = 11 * 4096
  + E *  256 = 14 *  256
  + E *   16 = 14 *   16
  + F *    1 = 15 *    1
  =                48879
А преобразование значения 666 в шестнадцатеричное делается так:
  666 / 16 = 41 r 10    ^
   41 / 16 =  2 r  9   /|\    Читаем цифры в этом направлении, не забывая преобразовывать
    2 / 16 =  0 r  2    |     в шестнадцатеричные цифры где потребуется.
  (Здесь стоп, так как результат 0)
  Шестнадцатеричное значение 666 равно 29A
В шестнадцатеричной системе хорошо то, что она позволяет преобразовывать в двоичную систему очень просто. Каждая шестнадцатеричная цифра представляет собой 4 бита, так что для преобразования между шестнадцатеричной и двоичной системами, вам нужно только преобразовать каждую шестнадцатеричную цифру в 4 бита или каждые 4 бита в одну шестнадцатеричную цифру (и учесть, что 4 общий делитель всех типичных значений длины двоичного числа в процессоре). Например:
  Шестн. число           5        9        D        F           4E
  Двоичное число      0101     1001     1101     1111       01001110
При использовании шестнадцатеричных значений в PureBasic, вы помещаете знак доллара впереди числа, например $BEEF.

Дополнительно, Описание из книги Зубкова С.В.

Двоичная система счисления.

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

10010110b = 1*27+0*26+0*25+1*24+0*23+1*22+1*21+0*20 =
= 128+16+4+2 = 150

Чтобы перевести десятичное число в двоичное, можно, например, просто делить его на 2, записывая 0 каждый раз, когда число делится на два, и 1, когда не делится (табл. 2).



Остаток Разряд
150/2 = 75
75/2 = 37
37/2 = 18
18/2 = 9
9/2 = 4
4/2 = 2
2/2 = 1
1/2 = 0
0
1
1
0
1
0
0
1
0
1
2
3
4
5
6
7
Результат: 10010110b

Таблица 2. Перевод числа из десятичной системы в двоичную


Чтобы отличать двоичные числа от десятичных, в ассемблерных программах в конце каждого двоичного числа ставят букву "b".

Биты, байты и слова

Минимальная единица информации называется битом. Бит может принимать только два значения — обычно 0 и 1. На самом деле эти значения совершенно необязательны — один бит может принимать значения "да" и "нет", показывать присутствие и отсутствие жесткого диска, является ли персонаж игры магом или воином — важно лишь то, что бит имеет только два значения. Но далеко не все величины принимают только два значения, а значит, для их описания нельзя обойтись одним битом.


Единица информации размером восемь бит называется байтом. Байт — это минимальный объем данных, который реально может использовать компьютерная программа. Даже чтобы изменить значение одного бита в памяти, надо сначала считать байт, содержащий его. Биты в байте нумеруют справа налево, от нуля до семи, нулевой бит часто называют младшим битом, а седьмой — старшим.

Так как всего в байте восемь бит, байт может принимать до 28 = 256 разных значений. Байт используют для представления целых чисел от 0 до 255 (тип unsigned char в С), целых чисел со знаком от -128 до +127 (тип signed char в С), набора символов ASCII (тип char в С) или переменных, принимающих менее 256 значений, например для представления десятичных чисел от 0 до 99. Следующий по размеру базовый тип данных — слово. Размер одного слова в процессорах Intel — два байта.


Биты с 0 по 7 составляют младший байт слова, а биты с 8 по 15 — старший. В слове содержится 16 бит, а значит, оно может принимать до 216 = 65 536 разных значений. Слова используют для представления целых чисел без знака со значениями 0 — 65 535 (тип unsigned short в С), целых чисел со знаком со значениями от -32 768 до +32 767 (тип short int в С), адресов сегментов и смещений при 16-битной адресации. Два слова подряд образуют двойное слово, состоящее из 32 бит, а два двойных слова составляют одно учетверенное слово (64 бита). Байты, слова и двойные слова — основные типы данных, с которыми мы будем работать.

В компьютерах, использующих процессоры Intel, все данные хранятся так, что младший байт находится по младшему адресу, так что слова записываются задом наперед, то есть сначала (по младшему адресу) записывают последний (младший) байт, а потом (по старшему адресу) записывают первый (старший) байт. Если из программы всегда обращаться к слову как к слову, а к двойному слову как к двойному слову, это не оказывает никакого влияния. Но если вы хотите прочитать первый (старший) байт из слова в памяти, придется увеличить адрес на 1. Двойные и учетверенные слова записываются так же — от младшего байта к старшему.

Шестнадцатеричная система счисления

Главное неудобство двоичной системы счисления — это размеры чисел, с которыми приходится обращаться. На практике с двоичными числами работают, только если необходимо следить за значениями отдельных бит, а когда размеры переменных превышают хотя бы четыре бита, используется шестнадцатеричная система. Эта система хороша тем, что она гораздо более компактна, компактнее десятичной, и тем, что перевод в двоичную систему и обратно происходит очень легко. В шестнадцатеричной системе используется 16 "цифр": 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, А, В, С. D, E, F, и номер позиции цифры в числе соответствует степени, в которую надо возвести число 16, так что:

96h = 9 * 16 + 6 = 150

Перевод в двоичную систему и обратно осуществляется крайне просто — вместо каждой шестнадцатеричной цифры подставляют соответствующее четырехзначное двоичное число:

9h = 1001b, 6h = 0110b, 96h = 10010110b

В ассемблерных программах при записи чисел, начинающихся с А, В, С, D, E, F, в начале приписывается цифра 0, чтобы нельзя было спутать такое число с названием переменной или другим идентификатором. После шестнадцатеричных чисел ставится буква "h" (см. табл. 3).


Десятичное Двоичное шестнадцатеричное
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0000b
0001b
0010b
0011b
0100b
0101b
0110b
0111b
1000b
1001b
1010b
1011b
1100b
1101b
1110b
1111b
10000b
00h
01h
02h
03h
04h
05h
06h
07h
08h
09h
0Ah
0Bh
0Ch
0Dh
0Eh
0Fh
10h

Таблица 3. Двоичные и шестнадцатеричные числа


Числа со знаком

Легко использовать байты, слова или двойные слова для представления целых положительных чисел — от 0 до 255, 65 535 или 4 294 967 295 соответственно. Чтобы использовать те же самые байты или слова для представления отрицательных чисел, существует специальная операция, известная как дополнение до двух. Для изменения знака числа выполняют инверсию, то есть заменяют в двоичном представлении числа все единицы нулями и нули единицами, а затем прибавляют 1. Например, пусть используются переменные типа слова:

150 = 0096h = 0000 0000 1001 0110b инверсия дает: 1111 1111 0110 1001b +1 = 1111 1111 0110 1010b = 0FF6Ah

Проверим, что полученное число на самом деле -150: сумма с +150 должна, быть равна нулю:

+150 + (-150) = 0096h + FF6Ah = 10000h ;

Единица в l6-м разряде не помещается в слово, и значит, мы действительно получили 0. В этом формате старший (7-й, 15-й, 31-й для байта, слова, двойного слова соответственно) бит всегда соответствует знаку числа 0 — для положительных и 1 — для отрицательных. Таким образом, схема с использованием дополнения до двух выделяет для положительных и отрицательных чисел равные диапазоны: -128 — +127 — для байта, -32 768 — +32 767 — для слов, -2 147 483 648 — +2 147 483 647 — для двойных слов.


Логические операции

Один из широко распространенных вариантов значений, которые может принимать один бит, — это значения "правда" и "ложь", используемые в логике, откуда происходят так называемые "логические операции" над битами. Так, если объединить "правду" и "правду" — получится "правда", а если объединить "правду" и "ложь" — "правды" не получится. В ассемблере нам встретятся четыре основные операции — И (AND), ИЛИ (OR), исключающее ИЛИ (XOR) и отрицание (NOT), действие которых приводится в таблице 4.


И ИЛИ ИСКЛЮЧАЮЩЕЕ ИЛИ НЕ
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
0 OR 0 = 0
0 OR 1 = 1
1 OR 0 = 1
1 OR 1 = 1
0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0
NOT 0 = 1
NOT 1 = 0

Таблица 4. Логические операции


Все эти операции побитовые, поэтому, чтобы выполнить логическую операцию над числом, надо перевести его в двоичный формат и выполнить операцию над каждым битом, например:

96h AND 0Fh = 10010110b AND 00001111b = 00000110b = 06h