Системы счисления

Что же такое система счисления и для чего она служит? Давайте разберемся с этими вопросами, т.к. понимание принципов и умение работать с системами счисления очень важны для программиста.

Что такое система счисления

Система счисления - это так называемая знаковая система, способ записи чисел с помощью знаков, именуемых цифрами.

Самой распространенной системой счисления в нашем мире является, конечно же, всем известная, десятичная система счисления (система с основанием 10). И не спроста - т.к. она имеет исторические корни. Одним из суждений является то, что наши предки использовали для счета пальцы рук, а их, как известно, десять. Я думаю, что вы все о ней конечно же слышали. Каждый день мы с ней сталкиваемся, т.к. практически все числовые записи, которые мы видим день ото дня, находятся именно в ней: цены в магазинах, номера страниц в любимой книге, практически весь курс арифметики и т.д. Так как эта система счисления десятичная, то, соответственно, в ней для записи чисел используется 10 знаков (цифр), а именно: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. Самая младшая цифра - это 0, а самая старшая - это 9 и она всегда на единицу меньше, чем основание (10).

Самыми распространенными системами счисления в мире компьютеров являются двоичная, восьмеричная, десятичная и шестнадцатеричная системы счисления.

Двоичная - это родная система счисления компьютера (основание 2). Она используется компьютером для врутреннего представления данных. На самом низком уровне вычисления в компьютере происходят именно в двоичной системе. Очень упрощенно можно сказать так: мы пишем программу компьютеру на языке С++, компилятор переводит ее в язык, понятный компьютеру, а именно в машинный язык, а затем уже этот код исполняется процессором. Причем каждому кусочку двоичного кода соответствует определенное действие процессора. Для записи чисел в этой системе счисления используется всего лишь две цифры: 0, 1. Самая младшая цифра - это 0, а самая старшая - это 1, она, как обычно, на единицу меньше, чем основание (2). Почему именно двоичная система стала родной для компьютера по всей вероятности связано с тем, что информация в компьютере передается по проводам в виде сигналов, которые кодируются в двоичной системе.

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

Восмеричная система счисления (основание 8) использует цифры от 0 до 7, а шестнадцатеричная использует цифры 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 и буквы A, B, C, D, E, F. Эти буквы являются, соответственно, эквивалентами чисел десятичной системы 10, 11, 12, 13, 14, 15. На рисунке ниже представлены соответствия чисел в рассмотренных системах счисления.

Позиционные системы счисления

Рассмотренные выше системы счисления являются позиционными, т.к. вес каждой цифры изменяется в зависимости от ее позиции в числе. Также у любой позиционной системы счисления есть свое основание, их мы с вами рассмотрели выше. Например, про десятичное число 537 (основание 10) можно сказать, что цифра 7 находиться в позиции единиц, цифра 3 - в позиции десятков, а цифра 5 - в позиции сотен. Также обратите внимание на то, что значения этих позиций равны основанию системы счисления (в нашем случае 10), возведенному в степень, начинающуюся с нулевой и увеличивающуюся на 1 при перемещении в числе на одну позицию влево.

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

Восьмеричное число 348 состоит из: 8 - находиться в позиции единиц, 4 - находиться в позиции восьми, 3 - находиться в позиции шестидесяти четырех.

Шестнадцатеричное число 8AF состоит из: цифра F расположена в позиции единиц, A - в позиции шестнадцати, а 8 - в позиции двухсот пятидесяти шести.

Программа перевода чисел различных систем счисления

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

//Программа перевода чисел из одной системы счисления в другую

#include <iostream>

const void print(int *, int);
void decMy(int, const int);
int inDecMy(const int, int);

using namespace std;

int main()
{
    int integer, bas, basis;

    cout << "Enter a basis 1(2...16): ";
    cin >> bas;
    cout << "Enter a integer: ";
    cin >> integer;
    cout << "Enter a basis 2(2...16): ";
    cin >> basis;

    if(bas >= 2 && bas <= 16 && basis >= 2 && basis <= 16)
    {
        if(bas == 10)
            decMy(integer, basis);
        if(bas != 10 && basis == 10)
            cout << inDecMy(bas, integer) << endl;
        if(bas != 10 && basis != 10)
            decMy(inDecMy(bas, integer), basis);
    }
    else
        cout << "Error! Basis = 2...16" << endl;
}

void decMy(int integer, const int basis)
{
    int count, temp = integer;

    for(count = 0; temp >= 1; count++)
        temp /= basis;

    int *ptr = new int[count];
    for(count = 0; integer >= 1; count++)
    {
        ptr[count] = integer % basis;
        integer /= basis;
    }

    print(ptr, count);
    delete []ptr;
}

int inDecMy(const int bas, int integer)
{
    int temp, total = 0, count = 1;

    while(integer >= 1)
    {
        temp = integer % 10;
        total = total + temp * count;
        count *= bas;
        integer /= 10;
    }

    return total;
}

const void print(int *ptr, int count)
{
    for(int i = count - 1; i >= 0; i--)
        switch(ptr[i])
        {
            case 10:
                cout << 'A';
                break;
            case 11:
                cout << 'B';
                break;
            case 12:
                cout << 'C';
                break;
            case 13:
                cout << 'D';
                break;
            case 14:
                cout << 'E';
                break;
            case 15:
                cout << 'F';
                break;
            default:
                cout << ptr[i];
                break;
        }
}

Теперь разберем программу перевода чисел систем счисления более детально.

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

cout << "Enter a basis 1(2...16): ";
cin >> bas;
cout << "Enter a integer: ";
cin >> integer;
cout << "Enter a basis 2(2...16): ";
cin >> basis;

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

if(bas >= 2 && bas <= 16 && basis >= 2 && basis <= 16)
{
    if(bas == 10)
        decMy(integer, basis);
    if(bas != 10 && basis == 10)
        cout << inDecMy(bas, integer) << endl;
    if(bas != 10 && basis != 10)
        decMy(inDecMy(bas, integer), basis);
}
else
    cout << "Error! Basis = 2...16" << endl;

Теперь давайте разберем функции. В программе их три, прототипы их находяться в самом верху программы, после подключения заголовочного файла

const void print(int *, int);
void decMy(int, const int);
int inDecMy(const int, int);

Для перевода числа из десятичной системы счисления в другую систему счисления используется функция decMy:

if(bas == 10)
    decMy(integer, basis);

Если нужно перевести число из любой системы счисления, кроме десятичной, в десятичную систему счисления, то вызывается функция inDecMy:

if(bas != 10 && basis == 10)
    cout << inDecMy(bas, integer) << endl;

В случае, если нужно перевести число не из десятичной системы счисления в недесятичную, то используем сразу две функции по такой схеме: сначала с помощью функции inDecMy приводим число к десятичному виду, а затем с помощью функции decMy, переводим из десятичной системы счисления в нужную.

if(bas != 10 && basis != 10)
    decMy(inDecMy(bas, integer), basis);

Разберем работу функции decMy. Хранить полученное число, в заданной системе счисления, будем в массиве, который создадим динамически. По основному правилу перевода из десятичной системы счисления в любую иную (смотреть рисунок ниже) переводим наше число, а именно: в первом цикле мы определяем количество цифр в числе, для того, чтобы знать размер массива, затем создаем сам массив и в следующем цикле уже помещаем остатки от деления в массив.

for(count = 0; temp >= 1; count++)
    temp /= basis;

Динамически создаем массив:

int *ptr = new int[count];

Остатки от деления помещаем в массив:

for(count = 0; integer >= 1; count++)
{
    ptr[count] = integer % basis;
    integer /= basis;
}

Выводиться полученное число с помощью функции print:

const void print(int *ptr, int count)
{
    for(int i = count - 1; i >= 0; i--)
        switch(ptr[i])
        {
            case 10:
                cout << 'A';
                break;
            case 11:
                cout << 'B';
                break;
            case 12:
                cout << 'C';
                break;
            case 13:
                cout << 'D';
                break;
            case 14:
                cout << 'E';
                break;
            case 15:
                cout << 'F';
                break;
            default:
                cout << ptr[i];
                break;
        }
}

Функция inDecMy переводит число в десятичную систему счисления путем разбивки исходного числа на позиции и соответствующего умножения разбитых разрядов на основание, возведенное в соответствующую степень. Затем эти числа складываются и получается искомое десятичное число. Рисунок ниже подробно иллюстрирует вышесказанное: