# Особенности представления и работы с действительными числами

Десятичный мир

Для начала представим себе, что данные в компьютерах хранятся в ячейках-"битах", каждое из которых может принимать 10 разных значений. В таком случае очень легко хранить положительные целые числа: каждое число по цифрам записывается в ячейки памяти. Реальный процессор может выполнять арифметические с такими числами, но есть проблема: чем больше цифр в числах, которые он сможет складывать за одну операцию (такт), тем сложнее его проектировать, тем больше тепла он выделяет и энергии потребляет. Поэтому необходимо выбрать некоторое фиксированную "стандартную" длину чисел так, чтобы с одной стороны для большей части основных задач числа туда помещались, с другой стороны были наиболее короткими. Например, можно выбрать длину в 10 цифр для "обычных" чисел и длину 20 для "длинных" (операций с длинными целыми числами за один такт процессора будет выполняться меньше). Кстати, нам потребуется хранить ещё и знак числа. Как лучше всего это сделать — вопрос очень хороший. Для простоты можно считать, что знак мы храним в старшей цифре: если старшая цифра 0, то число положительное, если 1 — то отрицательное.

Целые числа и дополнение до 10

В реальности используется несколько подходов к хранению знака числа, вернее даже к хранению просто целых чисел. Самый "популярный" в данный момент называется "дополнение до двойки" (two’s complement), что для нашего воображаемого десятичного процессора превращается в "дополнение до десятки". Основная идея подхода состоит в следующем. Так как наши числа ограничены 10-ю цифрами, то если в результате арифметической операции возникнет перенос через разряд в 11-ю цифру, то он будет потерян. В таких случаях говорят, что вычисления производятся по модулю 101010^{10}. Пусть у нас есть два числа: отрицательное xx и положительное yy, и нам нужно вычислить x+yx+y. Заметим, что по замечанию выше x+y(1010+x)+yx+y\equiv (10^{10}+x) + y (ведь добавление лишнего 101010^{10} ничего не меняет, у нас нет "места", чтобы хранить эту цифру). Но число (1010+x)(10^{10}+x) уже заведомо положительное.

Итак, ровно в этом состоит идея: для хранения отрицательного числа xx используется положительное число (1010+x)(10^{10}+x). Неотрицательные числа от 0000000000 до 4999999999 хранятся как есть. А числа от 5000000000 до 9999999999 отдаются отрицательным числам, причём 1-1 превращается в 10101=999999999910^{10}-1 = 9999999999, 2-2 превращается в 10102=999999999810^{10}-2 = 9999999998, и так далее, 5000000000-5000000000 превращается в... в 5000000000-5000000000. Заметим, что отрицательных чисел "поместилось" на одно больше, чем положительных.

Вот примеры: сложим 8 5128\,512 и 3 628-3\,628. 10103628=9 999 996 372.10^{10}-3628 = 9\,999\,996\,372. Далее 8 512+(3 628)8 512+9 999 996 372=10 000 004 8844 884.8\,512 + (-3\,628) \equiv 8\,512 + 9\,999\,996\,372 = 10\,000\,004\,884 \equiv 4\,884.
Сложим 6 460-6\,460 и 9 290-9\,290. (6 460)+(9 290)(10106 460)+(10109 290)=9 999 993 540+9 999 990 710=(-6\,460) + (-9\,290) \equiv (10^{10}-6\,460) + (10^{10}-9\,290) = 9\,999\,993\,540 + 9\,999\,990\,710 = =19 999 984 2509 999 984 2509 999 984 2501010=(15 750).= 19\,999\,984\,250 \equiv 9\,999\,984\,250 \equiv 9\,999\,984\,250 - 10^{10} = (-15\,750).

В чём выгода такого подхода? Во-первых, используются все возможные значения (если знак хранить в первой цифре, то будут "потеряны" 80% чисел). Во-вторых, с таким подходом отрицательные числа ничем не отличаются от положительных и не требуется усложнения схем для организации арифметических операций с ними. По модулю 101010^{10} отлично работают все арифметические операции, поэтому работать будут и вычитание, и умножение.

В реальных чипах используется двоичная система счисления, но в остальном всё устроенно именно так. Один бит — это двоичная цифра. И существуют числа разной длины — в 8, 16, 32 и 64 двоичных цифры. Это зависит от реальных чипов.

Итак, мы научились хранить целые числа. А как хранить действительные числа?

Можно, например, хранить десять цифр целой части и десять цифр дробной. Идея очень простая, но не очень полноценная. Обычное число Авогадро 6.0210236.02\cdot10^{23} записать уже не получится.

Можно было хранить числитель и знаменатель дроби (рационального числа). Но кроме множества проблем это не решает проблемы с 6.0210236.02\cdot10^{23} (ведь придётся хранить очень большое число, а, значит, нужно хранить много цифр).

Но если нужно уметь работать с числами вида 6.0210236.02\cdot10^{23}, то может быть прямо так и хранить? Скажем, хранить 15 цифр числа (плюс 1 цифра на знак) и 3 цифры степени (плюс снова 1 цифра на знак степени), в сумме 20 цифр, или "стандартное" длинное число. В этом случае станет возможно работать с числами от 1099910^{-999} до 1099910^{999} с точностью 15 значащих цифр. Значащие цифры в такой записи называются мантиссой, а степень — экспонентой. Число Авогадро при этом можно было бы записать как 6021021602\cdot10^{21}.

Заметим, что число Авогадро в виде x10nx\cdot 10^n может быть записано по-разному: и как 6.0210236.02\cdot10^{23}, и как 0.60210240.602\cdot10^{24}, и как 6021021602\cdot10^{21}, и даже парой совсем неудобных способов: 60200000000000101060200000000000\cdot10^{10}, 0.00000060210300.000000602\cdot10^{30}. Желательно выбрать из всех таких представлений одно (каноническое). Кажется заманчивым хранить мантиссу в виде обычного целого числа, например хранить число Авогадро в виде 6021021602\cdot10^{21}. Но это не очень хорошо: в такой записи числа очень неудобно сравнивать: например попробуйте сравнить число Авогадро с числами 610236\cdot10^{23}, 62102262\cdot10^{22}, 602110206021\cdot10^{20} или 60199999999999101060199999999999\cdot10^{10}. Чтобы числа было удобно сравнивать (и ещё по нескольким причинам), используют следующий подход. Положительное число всегда можно домножить на такую степень десятки, чтобы целая часть лежала от 1 до 9. Ровно такое представление и используют для записи, оно называется нормализованным. Итак, каждое ненулевое число может быть представлено в нормализованном виде ±a0,a1a2a1310n\pm \overline{a_0,a_1a_2\ldots a_{13}\ldots}\cdot 10^n, где a00a_0\ne0. И примерно в этом виде его можно и хранить: знак числа, целая часть, 14 цифр после запятой, знак степени, три цифры степени, в сумме 20 цифр.

Сравнивать числа в такой записи очень просто: например, если числа положительны, то нужно сначала сравнить показатели степени, а в случае, если они равны, сравнить мантиссы.

Кстати, мы ещё пропустили число 0. Его обычно хранят в виде 01000\cdot10^0, тогда для сравнения его с другими числами не потребуется дополнительных ухищрений.

Реальный двоичный мир

В реальности используются двоичные процессоры. Однако с точки зрения идей в них всё устроено так же, как в описанном выше десятичном случае. Использование двоичной системы счисления для действительных чисел создаёт некоторые дополнительные особенности при работе с ними, однако с ними мы познакомимся чуть позже.
Последнее изменение: Суббота, 15 Август 2020, 02:35