Дата и время публикации:
Использование и применение
1. Использование
1.1 Принятые cоглашения
Целочисленные числа могут быть представлены в виде бинарного числа, записанного в виде нотации систем счисления – десятичной, восьмеричной или шестнадцатеричной.
При этом, к
двоичной системе счисления относится запись числа, которую предваряет '0b' или '0B', а затем следующие за ними цифры '0' или '1';
(Стандартом ISO/IEC и GNU C не поддерживается)
восьмеричной системе счисления относится запись числа, которую предваряет '0', а затем одно и более допустимых восьмеричных чисел '01234567';
шестнадцатеричной системе счисления относится запись числа, которую предваряет '0x' или '0X' и следуемых за ними шестнадцатеричных цифр '0123456789abcdef' или '0123456789ABCDEF';
десятичной системе счисления любая последовательность чисел, которая начинается с ненулевой цифры и/или следуемых за ней одной или более десятичных цифр ('0123456789').
1.2 Числа без знака
На заре зарождения программирования, как ведущей функциональной части в решение вычислительных задач, было принято объединять последовательно следующие друг за другом разряды в бинарную последовательность по 4-ре разряда.
Поэтому внутри данной бинарной группы варьируются значения в допустимом диапазоне значений 0...15 в десятичной системе счисления и 0-9, А,B,C,D,E,F в шестнадцатитеричной, что и показано в дампе 1.2.1.
Дамп 1.2.1
DEC. HEX. BIN. BIN to DEC ------------------------------------------------- 0 0x00 0b0000 = 0 + 0 + 0 + 0 1 0x01 0b0001 = 0 + 0 + 0 + 2^0 2 0x02 0b0010 = 0 + 0 + 2^1 + 0 3 0x03 0b0011 = 0 + 0 + 2^1 + 2^0 4 0x04 0b0100 = 0 + 2^2 + 0 + 0 5 0x05 0b0101 = 0 + 2^2 + 0 + 2^0 6 0x06 0b0110 = 0 + 2^2 + 2^1 + 0 7 0x07 0b0111 = 0 + 2^2 + 2^1 + 2^0 8 0x08 0b1000 = 2^3 + 0 + 0 + 0 9 0x09 0b1001 = 2^3 + 0 + 0 + 2^0 10 0x0A 0b1010 = 2^3 + 0 + 2^1 + 0 11 0x0B 0b1011 = 2^3 + 0 + 2^1 + 2^0 12 0x0C 0b1100 = 2^3 + 2^2 + 0 + 0 13 0x0D 0b1101 = 2^3 + 2^2 + 0 + 2^0 14 0x0E 0b1110 = 2^3 + 2^2 + 2^1 + 0 15 0x0F 0b1111 = 2^3 + 2^2 + 2^1 + 2^0
Отсюда, как иллюстрирует дамп 1.2.1, наибольшее (недостижимое) значение в рассматриваемой бинарной группе разрядов будет являться значение 16 в десятичной системе счисления. При этом, одновременно, оно является основанием степени \(16^n\) , так же как и базовым значением для шестнадцатитеричной системы отсчета, а показатель степени соответствует нумерации бинарной группы 0,1,2,…,n . Для данного примера, оперирующего размерностью 8 бит, n будет равняться 2, что и иллюстрирует дамп 1.2.2 .
Дамп 1.2.2
--------------------------------------------------------------------- BIN. HEX. HEX to DEC DEC --------------------------------------------------------------------- 0 0001 0000. 0x10. 0*(16^2) + 1*(16^1) + 0*(16^0) = 16 + 0 = 16 0 0001 0001. 0x11. 0*(16^2) + 1*(16^1) + 1*(16^0) = 16 + 1 = 17 … … … … 0 0001 1010. 0x1A. 0*(16^2) + 1*(16^1) + 10*(16^0) = 16 + 10 = 26 … … … … 0 1111 1111. 0xFF. 0*(16^2) + 15*(16^1)+ 15*(16^0) = 240+15 = 255 1 0000 0000 0x100 1*(16^2) + 15*(16^1)+ 15*(16^0) = 1+240+15 = 256
В тоже время, как показывает дамп 1.2.2, для двух бинарных групп наиболее значащий бит (англ. Most Signed Bit – MSB) будет иметь значение 256, что соответствует степени \(16^2\), а степень \(16^1\) – значению 16. Таким образом, MSB для нулевой бинарной группы, составляющей 4 бита, будет равен значению 16, а для первой – значению 256, т.е. выражению:
(1) \(MSB = 16^n \).
При условии, что наименьший значащий бит (англ. Least Signed Bit – LSB) равен 1, тогда это утверждение будет верным. В обратном случае, нужно исходить из выражения:
(2) \(MSB = LSB * 16^n\),
из которого видно, что действительное значение от кодированного рассчитывается, при \( LSB = 1 \), так же согласно формуле (1).
Но, если исходить, что \(LSB = 0.0625\), то MSB будет равен 16, т.к. работает формула (2): \(0.0625 * (2^8) = 0.0625 *256 = 16\). Не верующим в силу арифметики предлагаю взглянуть на следующие зависимости, приведенные в дампе 1.2.3.
Дамп 1.2.3
------------------------------------------------------------ BIN. FLT NUMBER. CODE ------------------------------------------------------------ 0b0000 0001 0,0625. 1 ... ... ... 0b0000 0010. 0.125. 2 ... ... ... 0b0001 0000. 1,000. 16 ... ... ... 0b0001 1111. 1,937. 31 ... ... ... 0b0010 0000. 2,000. 32 ... ... ... 0b0011 0000. 3,000. 48 ... ... ... 0b0100 0000. 4,000 64 ... ... ... 0b0101 0000. 5,000. 80 ... ... ... 0b0110 0000. 6,000. 96 ... ... ... 0b0111.0000. 7,000. 112 ... ... ... 0b1000 0000 8,000. 128 ... ... ... 0b1001 0000. 9,000. 144 ... ... ... 0b1010 0000. 10,000 160 ... ... ... 0b1010 1111. 10,937 175 ... ... ... 0b1011 0000. 11,000. 176 ... ... ... 0b1100 0000. 12,000. 192 ... ... ... 0b1101 0000. 13,000. 208 ... ... ... 0b1110 0000. 14.000. 224 ... ... ... 0b1111 0000 15,000. 240 ... ... ... 0b1111.1111. 15,937. 255 0b10000 0000. 16,000 256
Как показано в дампе 1.2.3, исходя из приведенной последовательности видно, что на всем диапазоне целочисленных значений \(1,...,256\) сохраняются равно пропорциональные значения с ожидаемой разницей в значениях, равной 16.
Так же, были сделаны три реперные точки с числами, содержащие некоторые значения после запятой, из которых следует, что на значениях с дискретность \(1/16 = 0.0625\), достоверность составляет не более одного знака после запятой.
Поэтому, для обеспечение высокой достоверности для \(MSB=2048\) до 2-го знака потребуется уменьшение шага дискретности до \(1/256\) с расширением размерности слова до 32 разрядов, а для 3-его -- \(1/4096\) и не менее 64 разряда.
Таким образом, для получение значения LSB следует рассчитать по формуле:
(3) \( LSB = 1/(16^n) \),
где n – совпадает с числом знаков после запятой, достоверность которых нужно обеспечить.
1.3 Числа со знаком
Делим пополам найденное ранее в п.1.2 значение \( MSB=256 \) и получаем, что \( MSB/2 = 128 \), которое будет являться наименьшим отрицательным числом (англ. least negative number). При этом, наибольшее отрицательное значение (англ. most negative number) будет равно \( -1 \rightarrow (2^n)-1 = 255 \). Таким образом, ряд отрицательных чисел можно представить как показано в дампе 1.3.1 .
Дамп 1.3.1
------------------------------------------------------------ BIN. HEX. SDEC. DEC BIN to DEC ------------------------------------------------------------ 0b0111 1111. 7F. 127 127 2^7+...+2^0 ... ... ... ... ... ... 0b0000 0001. 1. 1. 1 2^0 ... ... ... ... ... ... 0b1111 1111. FF. -1. 255 2^7+...+2^0 ... ... ... ... ... ... 0b1000 0000. 80. -128. 128 2^7+...+ 0 ------------------------------------------------------------
Как следует из приведенного ряда чисел с учетом знака (см. столбец SDEC), отнимая единицу от половины максимального значимого числа, согласно формуле \( (MSB/2)-1 \), получим максимальное положительное число (англ. most positive number) равное значению 12710.
При этом, если отсортировать значения числового ряда по двоичным(бинарным) кодам (столбец BIN), получим, что для отрицательных и положительных чисел последовательность отсчетов кодов следует в обратном направлении.
2. Применение
2.1 В стандартах языка программирования ISO/IEC и GNU С для представления чисел из символьной записи (нотации) в бинарный формат данных (без учета знака), декларируемых типом unsigend long int, используется функция strtoul, прототип которой приведен в листинге 2.1.1 .
Дамп 2.1.1
1 #include <stdlib.h> . . . 5 unsigned long int strtoul(const char * restrict nptr,char ** restrict endptr,int base); 6 unsigned long long int strtoull(const char * restrict nptr,char ** restrict endptr,int base); . . .
Стандартом определены две функции strtoul() и strtoull(), которые возвращают приведенные к типу символьные значения чисел unsigend long int и unsigend long long int, соответственно.
При этом, ход конвертации переданного символьные значения числа в первом аргументе nptr осуществляется согласно выбранному способу записи, который задается в третьем аргументе base, так для
Восьмеричной системы счисления – \( base=8 \),
Десятичной системы счисления – \( base=10 \),
Шестнадцатеричной системы счисления – \( base=16 \) .
. . . 28 char *numbersp[] = { "0b1101", 29 "13", 30 "015", 31 "0x0D", NULL }, 32 **p = numbersp; 33 34 fprintf ( stdout, "Convert library function strtoul: \n" ); 35 fflush ( stdout ); 36 while ( *p ) 37 { 38 unsigned long r = strtoul ( *p, NULL, 0 ); 39 fprintf ( stdout, "\"%-8s\": %02lu\n", *p++, r ); 40 } 41 fprintf ( stdout, "done.\n"); . . .На выходе, должны получить нечто подобное, как показано в дампе 2.1.1.1 Дамп 2.1.1.1
Convert library function strtoul(3): 0b1101 : 00 13 : 13 015 : 13 0x0D : 13 done.При этом, как видно из указаного дампа, способ двоичной записи стандартом ISO/IEC и GNU С не поддерживается – на что указывает нулевое значение в правой части строки после двоеточия для символьного представления числа 13 в двоичной системе счисления "0b1101".
2.2 Для чистоты эксперимента, вместо приведенной выше функция strtoul(), будем конвертировать самостоятельно, как это показано в листинге 2.2.1.
Листинг 2.2.1
… 135 size_t libintops_rsltbl_prnt ( void ) 136 { 137 unsigned int dec = 0; 138 char *bg_p = TBLHEAD_PRN(libintops_rslt_tbl,head_const), 139 *go = bg_p; 140 for ( ; dec < 16 && ( (go-bg_p) < TBL_MAXSIZE ); dec++ ) 141 { 142 /* добавляет строку, содержащую несколько значений, представленных следующими системами счисления: */ 143 go = DECNUM_PRN(go,dec); /* десятичной */ 144 *go++ = '\t'; 145 go = HEXNUM_PRN(go,dec); /* шестнадцатиричной */ 146 *go++ = '\t'; 147 go = BINUM_PRN(go,dec); /* двоичной. */ 148 *go++ = '\t'; 149 *go++ = '='; 150 *go++ = ' '; 151 go = BINPOWER_PRN(go,dec); /* А также возведения в степень */ 152 *go++ = '\n'; 153 } 154 *go = '\0'; 155 return (go-bg_p); 156 } …
Функция intops_rsltbl_prnt(), приведенная в листинге 2.2.1, осуществляет формирование таблицы для вывода целого числа, приведенного в дампе 2.2.1, согласно принятой, ранее в п.1.1, нотациями для шестнадцатеричного, двоичного и десятичного числа.
При этом, в листинга 2.2.1 приводится приведение числа к текстовому формату в
Строке 143
для печати десятичного, как в printf(3) для формата "%d", вызывается оператором-выражением DECNUM_PRN();
37 #define DECNUM_PRN(t,dec) ({ \
38 unsigned int __d = (dec); \
39 char * __t = (t), *__g = (__t); \
40 if( (10 <= (__d) && (__d) <= 15 ) ) \
41 { \
42 *(__g)++ = 0x31; \
43 *(__g)++ = 0x30 + (__d) - 0x0a; \
44 } \
45 else \
46 { \
47 *(__g)++ = 0x30 + (__d); \
48 } \
49 __g; })
В строках 40-48 производится разложение значений, присваиваемых переменной dec в функции libintops_rsltbl_prnt(), на до и после 10-ти, тем самым обеспечивая печать единицы в диапазоне значений с 10 по 15.
Строке 145
для печати шестнадцатеричного, как в printf(3) для формата "%X", вызывается оператором-выражением HEXNUM_PRN();
51 #define HEXNUM_PRN(t,dec) ({ \
52 unsigned int __d = (dec); \
53 char * __t = (t), *__g = (__t); \
54 *(__g)++ = '0'; \
55 *(__g)++ = 'x'; \
56 *(__g)++ = '0'; \
57 if( (10 <= (__d) && (__d) <= 15 ) ) \
58 { \
59 *(__g)++ = 0x41 + (__d) - 0x0a; \
60 } \
61 else \
62 { \
63 *(__g)++ = 0x30 + (__d); \
64 } \
65 __g; })
В строках 51-62 производится разложение значений, присваиваемых переменной dec в функции libintops_rsltbl_prnt(), на до и после 10-ти, тем самым обеспечивая печать символов от A по F в диапазоне значений с 10 по 15.
Строке 147
для печати двоичного, вызывается оператором-выражением BINUM_PRN();
67 #define BINUM_PRN(t,dec) ({ \ 68 int __b = 3; \ 69 unsigned int __d = (dec); \ 70 char * __t = (t), *__g = (__t); \ 71 for ( ; (__b) >= 0; (__b)-- ) \ 72 { \ 73 if ( (__b) == 3 ) { \ 74 *(__g)++ = 0x30; \ 75 *(__g)++ = 'b'; \ 76 } \ 77 *(__g)++ = 0x30 + (((__d) >> (__b)) & 1); \ 78 } \ 79 __g; })В строках 64-76 производится разложение на отдельные разряды с использованием цикла for- с отсчетом в обратном порядке. При этом, на 3-ем разряде в строках 70-73 печатается "0b" признак представления числа в двоичной системе.
Строке 151
для печати двоичного, вызывается оператором-выражение BINPOWER_PRN();
Листинг 2.2.1.4
81 #define BINPOWER_PRN(t,dec) ({ \
82 int __b = 3; \
83 unsigned int __d = (dec); \
84 char * __t = (t), *__g = (__t); \
85 for ( ; (__b) >= 0; (__b)-- ) \
86 { \
87 if( (((__d) >> (__b)) & 1) ) \
88 { \
89 *(__g)++ = '2'; \
90 *(__g)++ = '^'; \
91 *(__g)++ = 0x30 + (__b); \
92 } \
93 else \
94 { \
95 *(__g)++ = ' '; \
96 *(__g)++ = 0x30; \
97 *(__g)++ = ' '; \
98 } \
99 if( (__b) != 0 ) \
100 { \
101 *(__g)++ = ' '; \
102 *(__g)++ = '+'; \
103 *(__g)++ = ' '; \
104 } \
105 } \
106 __g; })
В строках 78-104 производится разложение на отдельные разряды с использованием цикла for- с отсчетом в обратном порядке. При этом, если разряд содержит "1" на печать выводится \( 2 ^ n \), где n содержит порядковый номер разряда, начиная с нуля, а при значении равном "0" соответственно печатается символ нуля.
В результате выполнение кода, приведенного в листинге 2.2.1, должно получится нечто похожее, как иллюстрирует дамп 1.2.1.
3. Библиография
3.1 A Tutorial on Data Representation Integers, Floating-point Numbers, and Characters
3.4 Binary: signed numbers (positive and negative)
3.5 most significant bit or byte
3.6 C11 standard (ISO/IEC 9899:2011): §7.22.1.4 "The strtol, strtoll, strtoul, and strtoull functions", 344-345 pages