× К оглавлению На главную Об авторе

Дата и время публикации:

Использование и применение


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

Примечание Запись выражения 2^3, 2^2, 2^1 или 2^0 и т.п. в дампе 1.2.1 соответствует операции возведения в степень, где число 2 перед специальным символом '^' является основанием, а числа последовательного ряда 0,1,2,3 и т.д., следующие после, относятся к показателю степени.

Отсюда, как иллюстрирует дамп 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 \) .

Примечание Если \( base=0 \), то переход от символьной формы в бинарную должно производится согласно ранее представленным правилам в п.1.1. В листинге 2.1.1.1 приводится пример такого использования strtoul(). Листинге 2.1.1.1
. . .
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();

Реализация Перевод из бинарного числа в десятичную нотацию, показано в листинге 2.2.1.1 . Листинг 2.2.1.1
 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();

Реализация Перевод из бинарного числа в шестнадцатеричную нотацию, предваряемой "0x", показано в листинге 2.2.1.2 . Листинг 2.2.1.2
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();

Реализация Двоичная нотация, предваряемая "0b", как ранее убедились в п.2.1, не определена стандартом и не может использоваться без прямого конвертирования, как показано в листинге 2.2.1.3 . Листинг 2.2.1.3
 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 .
Листинг 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.2 Understanding MSB LSB

3.3 Least significant bit

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