11,8K подписчиков

О представлении чисел в ЭВМ. Когда запятая плавает

733 прочитали

Представление рациональных чисел в виде обыкновенных дробей удобно и полезно, но для нас все таки более привычны десятичные дроби. И их представление в виде чисел с фиксированной запятой позволяет работать с ними быстро и удобно. Но все таки, числа с фиксированной запятой не в состоянии покрыть все потребности в числовой обработке обработке.

Проблема возникает при работе с очень большими или очень малыми числами. А если еще учесть, что нужно выполнять арифметические операции с числами из всего диапазона... Это неудобно даже на бумаге. Но математики, разумеется, нашли решение этой проблемы. И называется оно

Научная, или экспоненциальная, форма записи чисел

С тапкой формой записи все знакомятся еще в школе, но все таки, полезно будет напомнить, что это такое.

Давайте представим, что нам нужно измерять и записывать вес различных предметов. Сразу возникает вопрос, записывать будем в граммах, килограммах, центнерах, тоннах? Но мы же знаем, что все несколько проще, так как 1 кг это 1000 г, а 1 ц это 100 кг, и так далее. Поэтому легко можем записать измеренный вес в любых единицах измерения. Например,

123 г = 0.123 кг = 0.00123 ц = 0.000123 т

1.5 т = 1500 кг = 1500000 г

То есть, фактически у нас есть некоторое число и масштабный множитель. И мы можем записать, например, так

123 г = 123 / 1000 кг = 123 / 100000 ц

1.5 т = 1.5 * 1000 кг = 1.5 * 1000000 г

Но записывать в таком виде неудобно. Однако, масштабный множитель у нас это число 10 в некоторой целой степени. И мы вполне можем, вместо длинного ряда нулей, просто записывать эту степень масштабного множителя. Будет гораздо короче. Особенно, для очень больших и очень малых чисел

Экспоненциальная, научная, форма записи чисел
Экспоненциальная, научная, форма записи чисел

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

Поэтому стандартная, математическая, запись и оказалась проблематичной. Выход был найден в виде несколько измененных правил записи. Например, можно было записывать так

0.123 = 123 * 10 ^ -3

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

0.123 = 123Е-3 = 123е-3

То есть, мантисса и порядок, без явного указания основания системы счисления, так как оно всегда 10. Буква Е, которая отделяет мантиссу от порядка, является сокращением от аббревиатуры exp - экспонента.

Нормализованная форма записи

Рассмотренный ранее пример записи числа 0.123 в виде 123е-3 можно записать и по другому

123е-3 = 1.23е-1 = 12.3е-2

Причем все варианты соответствуют одному и тому же числу, разница только в масштабном множителе - порядке. Для исключения неоднозначности было введено понятие нормализованной формы записи.

В нормализованной форме записи мантисса должна находиться в диапазоне

1 ≤ m < 10

Другими словами, мантисса, рациональное число в десятичной форме записи, должно иметь целую часть в виде цифры от 1 до 9. Исключением является число в точности равное 0. Поэтому нормализованной формой записи будет именно

1.23е-1

Почему запятая плавающая?

Давайте ненадолго вернемся к рациональным числам в форме с фиксированной запятой. Почему в них запятая именно "фиксированная"? Ведь мы свободно могли разместить запятую (десятичную точку) в любом месте числа. Это немного усложняло операции с числами у которых запятая находится в разных позициях, но не более того.

Все правильно. Поскольку числа с фиксированной запятой это рациональные числа записанные в виде десятичной дроби, мы вместо знаменателя записывали (хранили как атрибут числа) количество цифр после запятой. Это просто несколько иная форма записи, и не более того. Число оставалось рациональным.

Фиксированность запятой при этом заключается в том, что в результате операции положение запятой не могло измениться. Запятая оставалась ровно в том же месте, что и в числах участвующих в операции

Представление рациональных чисел в виде обыкновенных дробей удобно и полезно, но для нас все таки более привычны десятичные дроби.-2

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

123.5 / 1000000 = 0.0

Как же так? Результат не должен быть равным 0.0! Он и не равен, он просто не может быть представлен в виде числа с фиксированной запятой с одной цифрой после в дробной части.

Если мы разрешим запятой (десятичной точке) свободно изменять свое положение, все встает на свои места

123.5 / 1000000 = 0.0001235

Как видно, положение запятой действительно изменилось. Именно это и позволило показать все цифры в дробной части результата. Вот это и называется плавающей запятой. На самом деле, в обычной жизни нам привычна именно запись чисел в форме с плавающей запятой, а не с фиксированной.

Преставление чисел с плавающей запятой и научная форма

В форме с плавающей запятой записываются, как мы уже знаем, рациональные числа. А в ЭВМ мы хранили рациональное число как пару чисел, числитель и знаменатель. Для рациональных чисел записанных в виде десятичной дроби можно было хранить не знаменатель, а количество цифр после запятой.

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

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

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

Мы можем, если процессор не поддерживает работу с числами с плавающей запятой, хранить число с плавающей запятой в виде структуры (для языка С, например). Но сегодня нам более интересен именно аппаратный формат представления таких чисел в ЭВМ. А значит, мы переходим от десятичной системы счисления к двоичной.

Естественно, число будет представлено в виде последовательности байт. Однако, если для обыкновенных дробей не имело особого значения, в каком именно порядке хранятся числитель и знаменатель, для чисел с плавающей запятой небольшая разница все таки есть. И более оптимальным является вот такое представление

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

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

Количество разрядов мантиссы значительно больше, чем количество разрядов порядка. Ведь именно мантисса определяет, насколько точно представлено собственно число. Порядок е определяет лишь диапазон представляемых чисел.

Как и у десятичных чисел, мантисса двоичных чисел должна соответствовать определенным правилам. Целая часть двоично числа обязательно должна быть равна 1. Конечно, за исключением числа в точности равного 0. А это означает, что после выполнения операции с числами с плавающей запятой может потребоваться нормализация.

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

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

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

Сегодня распространен формат представления чисел с плавающей запятой IEEE-754, который изначально был принят в 1985 году. На этом стандарте основан и международный стандарт ISO 60559: 2011. Но до сих пор в некоторых процессорах могут использоваться (и используются) собственные, нестандартные, форматы. А внутреннее представление в процессорах чаще всего именно нестандартное. Так в процессоре 80487 внутренне представление занимало 80 бит.

Давайте, очень кратко, посмотрим, как представлено число с плавающей запятой одинарной точности в стандарте IEEE-754

Машинное представление числа одинарной точности с плавающей запятой в стандарте IEEE-754
Машинное представление числа одинарной точности с плавающей запятой в стандарте IEEE-754

Такое число занимает 32 бита, то есть, 4 байта. В младших разрядах, мы уже знаем, почему, размещается мантисса, которая занимает 24 бита. Почему же на иллюстрации показано 23 бита? Нет, это не ошибка. Вспомните, что целая часть мантиссы, ее старший разряд в двоичном виде, обязательно должна равняться 1. А значит, этот бит можно просто не хранить. Вот так и получается, что мантисса занимает 24 бита, из которых хранятся только 23.

Порядок занимает 8 бит. И он оказывается не выровненным на границе байта. Последний бит, самый старший для числа в целом, как наборе бит, занимает знак мантиссы, который является и знаком собственно числа. Почему знак числа вынесен отдельно от мантиссы? Разве не проще было хранить вместе с мантиссой, как это принято для целых чисел? Тогда и порядок был бы выровнен на границе байта.

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

Да, это приводит к появлению двух нулей, +0 и -0, как мы уже рассматривали ранее. Я не знаю, зачем так было принято в стандарте. Но это суровая реальность. Если учесть, что при принятии стандарта соревновались несколько различных форматов, причем активными участниками процесса были DEC (уже на излете) и Intel, можно предположить, что вмешалась политика (в виде корпоративных войн).

Возникает вопрос, куда же делся знак порядка? Его действительно нет в явном виде. Но при этом знак все таки есть. Просто порядок хранится со смещением +127. То есть, хранимый порядок равен фактическому порядку увеличенному на 127 (десятичное).

Заключение

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

Работа с числами с плавающей запятой более сложна, требует значительно больше ресурсов, занимает больше времени. Но такие числа очень важны для научных и инженерных расчетов. Поэтому, даже те ЭВМ, процессоры которых не поддерживают работу с такими числами на аппаратном уровне, могут поддерживать подключение дополнительного процессора, или дополнительного блока в состав процессора, если обработка чисел с плавающей запятой действительно необходима. Примером могут служить сопроцессоры в всем известных IBM-PC.

Приглашаю посетить дружественный канал

До новых встреч!