параметры вещественных типов, такие как диапазон и точность, можно найти в справке Delphi.
3.2.3. Внутренний формат вещественных чисел
Рассмотрим тип
Single
, т.к. он самый короткий и, следовательно, самый простой для понимания. Остальные типы отличаются от него только количественно. В дальнейшем числа в формате
Single
мы будем записывать как s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm, где s означает знаковый бит, е — бит экспоненты, m — бит мантиссы. Порядок хранения битов в типе Single показан на рис. 3.1, б (по принятым в процессорах Intel правилам байты в многобайтных значениях переставляются так. что младший байт идет первым, а старший — последним, и вещественных чисел это тоже касается В мантиссе хранится двоичное число. Чтобы получить истинное значение мантиссы, к ней надо мысленно добавить слева единицу с точкой (т.е., например, мантисса 1010000000000000000000 означает двоичную дробь 1.101). Таким образом, имея 23 двоичных разряда, мы записываем числа с точностью до 24-х двоичных разрядов.
Экспонента — по определению всегда целое число. Но способ записи экспоненты в вещественных числах не совпадает с рассмотренным ранее способом записи чисел со знаком. Ноль в этом представлении записывается как 01111111 (в обычном представлении это равно 127). Соответственно. 10000000 (128 в обычном представлении) означает единицу, а 01111110 (126) означает -1, и т. д. (т.е. из обычного беззнакового числа надо вычесть 127, и получится число, закодированное в экспоненте). Такая запись чиста называется нормализованной.
Из описанных правил есть исключения. Так, если все биты экспоненты равны нулю (т.е. там стоит число -127), то к мантиссе перед ее началом надо добавлять не "1.", а "0." (денормализованная запись). Это позволяет увеличить диапазон вещественных чисел. Если бы этого исключения не было, то минимально возможное положительное число типа
Single
было бы равно примерно 5,9·10– 39. А так появляется возможность использовать числа до 1,4·10– 45. Побочным эффектом этого является то, что числа, меньшие чем 1,17·10– 38, представляются с меньшей, чем 24 двоичных разряда, точностью. Если все биты в экспоненте равны единице, а в мантиссе — нулю, то мы получаем комбинацию, известную как
INF
(от англ. Infinity — бесконечность). Эта комбинация используется тогда, когда результат вычислений превышает максимально допустимое форматом число. В зависимости от значения бита s бесконечность может быть положительной или отрицательной. Если же при такой экспоненте в мантиссе хоть один бит не равен нулю, такая комбинация называется
NAN
(Not A Number — не число). Попытки работы с комбинациями
NAN
или
INF
приводят к ошибке времени выполнения.
Для задания нуля все биты мантиссы и экспоненты должны быть равны нулю (формально это означает 0·10– 127). С учетом описанных правил, если хотя бы один бит экспоненты не будет равен нулю (т.е. экспонента будет больше -127), запись будет считаться нормализованной, и нулевая мантисса будет рассматриваться как единица. Поэтому никакие другие комбинации значений мантиссы и экспоненты не могут дать ноль.
Тип
Double
устроен точно так же, разница только в количестве разрядов и в том, какое значение экспоненты берется за ноль. Итак, мы имеем 11 разрядов для экспоненты. За ноль берется значение 1023.
Несколько иначе устроен
Extended
.
Кроме количественных отличий добавляется еще и одно качественное: в мантиссе явно указывается первый разряд. Это означает, что мантисса 1010… интерпретируется как 1.01, а не как 1.101, как это было в типах
Single
и
Float
. Поэтому если 23-битная мантисса типа
Single
обеспечивает 24-знаковую точность, а 52-битная мантисса
Double
— 53-битную, то 64-битная мантисса
Extended
обеспечивает 64-х, а не 65-битную точность. Соответственно, при денормализованной форме записи первый разряд мантиссы явно содержит 0. За ноль экспоненты принимается значение 16 383.
Тип
Real
, как уже упоминалось, стоит особняком. Во-первых, в нем биты следуют в другом порядке, а во-вторых, нет денормализованной формы. Мы не будем касаться внутреннего устройства типа
Real
, т.к. эта информация уже перестала быть актуальной.
3.2.4. "Неполноценный" Extended
Ранее мы отметили, что FPU всегда выполняет все операции в формате
Extended
, оговорившись при этом, что есть исключение из этого правила. Здесь мы рассмотрим это исключение.
У FPU существует специальный двухбайтный регистр, называемый управляющим словом. Установка отдельных битов этого регистра диктует то или иное поведение при выполнении операций. Прежде всего, это связано с тем, какие исключения может возбуждать FPU. Другие биты этого регистра отвечают за то, как будут округляться числа, как FPU понимает бесконечность, — всё это можно при необходимости узнать из документации Intel. Нас же будут интересовать только два бита из этого слова: восьмой и девятый. Именно они определяют, как будут обрабатываться числа внутри сопроцессора.
Если восьмой бит содержит единицу (так установлено по умолчанию), то десять байтов внутренних регистров сопроцессора будут задействованы полностью, и мы получим "полноценный"
Extended
. Если же этот бит равен нулю, то все определяется значением бита 9. Если он равен единице, то используется только 53 разряда мантиссы (остальные всегда равны нулю). Если же этот бит равен нулю — только 24 разряда мантиссы. Это увеличивает скорость вычислений, но уменьшает точность. Другими словами, точность работы сопроцессора может быть понижена до типа
Double
или даже
Single
. Но это касается только мантиссы, экспонента в любом случае будет содержать 15 бит, так что диапазон типа
Extended
сохраняется в любом случае.
Для работы с управляющим словом сопроцессора в модуле
System
описана переменная
Default8087CW
типа
Word
и процедура
Set8087CW(CW: Word)
. При запуске программы в переменную
Default8087CW
записывается
то управляющее слово, которое установила система при запуске программы. Функция
Set8087CW
одновременно записывает новое значение в управляющее слово и в переменную
Default8087CW
.
Такое поведение этой функции не всегда удобно — иногда бывает нужно сохранить старое значение переменной
Default8087CW
(впрочем, это несложно сделать, заведя дополнительную переменную). С другой стороны, если значение управляющею слова изменить, не используя
Set8087CW
(а в дальнейшем мы увидим, что такие изменения могут происходить помимо нашей воли), то с помощью функции
Default8087CW
просто нет возможности узнать текущее значение управляющего слова. В Delphi 6 и выше появилась функция
Get8087CW
, позволяющая узнать значение именно контрольного слова, а не переменной
Default8087CW
. В более ранних версиях существовал единственный способ получить значение этого слова — встроенный в Delphi ассемблер.