close

Вход

Забыли?

вход по аккаунту

?

Lekarev1

код для вставкиСкачать
ФЕДЕРАЛЬНОЕ АГЕНТСТВО ПО ОБРАЗОВАНИЮ
Государственное образовательное учреждение
высшего профессионального образования
САНКТПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ
АЭРОКОСМИЧЕСКОГО ПРИБОРОСТРОЕНИЯ
М. Ф. Лекарев
ДАННЫЕ
С ПЛАВАЮЩЕЙ ТОЧКОЙ
Учебное пособие
СанктПетербург
2006
УДК 621.391
ББК 32.811
Л43
Рецензенты:
кафедра «Программное обеспечение вычислительной техники и систем инфор
мационной безопасности» Курганского государственного университета;
доктор технических наук, профессор А. А. Шалыто
Утверждено редакционноиздательским советом университета
в качестве учебного пособия
Л43
Лекарев М. Ф.
Данные с плавающей точкой: учеб. пособие / М. Ф. Лека
рев. ГУАП. – СПб., 2006. – 80 с.: 21 ил.
ISBN 5808802032
Рассмотрены идеи представления данных с плавающей точкой, ко
довые форматы стандарта IEEE 754, переменные и литералы с плаваю
щей точкой. Приведена программа на языке С/С++, предназначенная
для изучения кодовых форматов. Кратко обсуждаются вопросы разра
ботки и оформления программ.
Для студентов всех специальностей, изучающих программирова
ние на языках высокого уровня (C, C++, Java, C# и др.), а также для
аспирантов и специалистов в области вычислительных машин.
УДК 621.391
ББК 32.811
Учебное издание
Лекарев Михаил Федорович
ДАННЫЕ
С ПЛАВАЮЩЕЙ ТОЧКОЙ
Учебное пособие
Редактор А. Г. Ларионова
Верстальщик А. Н. Колешко
Сдано в набор 18.09.06. Подписано к печати 23.10.06. Формат 60 84 1/16.
Бумага офсетная. Печать офсетная. Усл. печ. л. 5,00. Уч.изд. л. 4,88.
Тираж 100 экз. Заказ №
Редакционноиздательский центр ГУАП
190000, СанктПетербург, Б. Морская ул., 67
ISBN 5808802032
2
©
©
ГУАП, 2006
М. Ф. Лекарев, 2006
ПРЕДИСЛОВИЕ
Один из важнейших элементов профессиональной квалификации
разработчика программного обеспечения (ПО) – наличие ясных пред
ставлений о выполняемой форме программы в памяти вычислитель
ной машины. Это особенно справедливо в отношении кода обрабаты
ваемых данных.
Данные с плавающей точкой используются практически во всех
областях применения вычислительных машин. Однако понятные и
полные сведения об их кодировании редко встречаются в учебниках
по программированию.
Пособие призвано восполнить отмеченный пробел. Оно представ
ляет собой законченный фрагмент учебного курса по одному из мно
гих языков программирования (например, C, C++, Java, C#).
3
1. ОБЩИЕ ЗАМЕЧАНИЯ
1.1. Кодирование и содержание
Одна из основных сложностей на начальных этапах изучения вы
числительных машин и программирования – осознание разницы меж
ду кодированием и содержанием.
Рассмотрим простой пример. Пусть задан код: цепочка десятич
ных цифр
010905
Что может означать этот код?
Кажется, что единственное возможное значение – это число «де
сять тысяч девятьсот пять». Но можно предложить и другие правила
интерпретации (правила декодирования) этой цепочки (рис. 1.1).
Используя правило 1, получим, что код 010905 означает «день
первое сентября 2005 года» (День Знаний), а используя правило 2,
получим, что код 010905 означает «день девятое января 1905 года»
(Кровавое Воскресенье русской истории).
Важная деталь: часть информации представлена элементами (знач
ками) кода в явном виде (например, номер месяца в году), а другая
часть информации подразумевается (например, две старшие цифры
номера года).
123
5
2 42
642652
17
123
5
2 1
642652
17
1
2
12345 617
34879
1
2
12345 34879
26
1
3
12345 34879
26
1
3
12345 617
34879
1
12345 26
1232 8247
1
4
12345 26
1532 8247
Рис. 1.1. Возможные правила декодирования цепочки 010905
4
4
Можно предложить и другие, более изощренные правила декоди
рования. Например: код 010905 означает «книга номер 01 на полке
09 стеллажа 05 некоторой (конкретной!) библиотеки». И тогда код
010905 может «скрывать в себе» и один из шедевров мировой лите
ратуры, и бульварный роман.
Так что же означает код 010905 «на самом деле»? Ответ: ничего
не означает (или, что то же самое: означает все, что угодно).
Таким образом, сам по себе код (цепочка символов) не означает
ничего. В справедливости этого утверждения легко убедиться, вспом
нив о так называемых «мертвых языках», правила интерпретации
которых утеряны человечеством.
Код приобретает конкретный смысл только в сочетании с конк
ретным правилом интерпретации (правилом декодирования). Очевид
но, что если код составлен по одним правилам, а декодируется по
другим, то изначальный смысл (изначальное содержание) теряется.
Эта «беллетристическая» преамбула в полной мере относится и
к цепочкам битов, которые хранятся в памяти вычислительных ма
шин.
1.2. Кодирование целых значений
Продолжим рассмотрение цепочек значков. Пусть имеется цепоч
ка из трех десятичных цифр и лидирующего знака ‘+’ или ‘–’. Шаб
лон такой цепочки показан на рис. 1.2, а. Символ ‘З’ шаблона озна
чает знак (‘+’ или ‘–’), а символ ‘Ц’ шаблона – произвольную деся
тичную цифру.
Если цепочку, составленную по этому шаблону, использовать для
представления целых чисел, то можно предложить правило интер
претации (рис. 1.2, б).
Термин «десятичная точка», использованный на рис. 1.2, озна
чает: «знак, который разделяет целую и дробную части числа». Де
сятичная точка никак не представлена фрагментом кода, а ее поло
жение подразумевается правилом декодирования.
Одну десятичную цифру можно закодировать сочетанием из четы
рех битов – от 0000 до 1001. Четыре бита, которые составляют поло
вину байта, называют тетрадой.
Для кодирования знака достаточно одного бита: например, бит 0
означает «плюс», а бит 1 означает «минус». В тетраде, которая пред
ставляет знак числа, при таком кодировании знака три бита оста
нутся неиспользованными.
Правило кодирования данных в памяти вычислительной маши
ны называется кодовым форматом. Таким образом, на рис. 1.2, б
5
а)
1
2
128
б)
1
2
2
2
128
2
2
12345678983
8 56
436 6
Рис. 1.2. Шаблон цепочки значков (а) и его возможное использование (б)
показан кодовый формат для представления целых значений. По
добный кодовый формат называется двоичнодесятичным кодом и
широко используется.
Код знака может принимать два корректных значения, а код каж
дой десятичной цифры – десять значений. Общее количество коррект
ных кодов составляет
2 * 10 * 10 * 10 = 2000
значений.
Конкретный код в этом кодовом формате позволяет представить
целые десятичные числа из следующего ряда:
–999, –998, ..., –000, +000, +001, ..., +998, +999
В этом ряду все элементы подчеркнуто показаны состоящими из
трех цифр – в соответствии с кодовым форматом.
Обеспечена возможность представления конечного подмножества
бесконечного множества целых чисел. При этом все представимые
значения представлены точно.
1.3. Кодирование вещественных значений
Очевидный недостаток рассмотренного в подразд. 1.2 кодового
формата для представления чисел состоит в том, что обеспечено пред
ставление только целых чисел (и притом, из сравнительно узкого
диапазона возможных значений).
Посмотрим, нельзя ли использовать код той же длины для пред
ставления как целых, так и нецелых чисел. Предложим следующую
форму представления:
± Ц . Ц * 10 ± Ц
6
Значащие цифры значения называются мантиссой (mantissa).
Показатель степени (у нас – степени при основании 10) называется
порядком (exponent).
При разработке кодового формата учтем, что и знак числа (ЗЧ) и
знак порядка (ЗП) можно закодировать одним битом каждый, так
что оба кода «поместятся» в одну тетраду. Получается кодовый фор
мат, который показан на рис. 1.3, а (знак «**» означает «возвести в
степень»).
Пример использования этого кодового формата для представле
ния значения +0.0012 показан на рис. 1.3, б.
На рис. 1.3 показано, что предполагаемое положение десятичной
точки среди цифр мантиссы фиксировано. Но в процессе интерпрета
ции кода точка сдвигается влево или вправо («плавает») в соответ
ствии со значением порядка. Поэтому представление значения в фор
ме пары чисел
( мантисса, порядок )
называется представлением с плавающей точкой (floatingpoint
representation).
13 12
4
4
1
4
23455
12345662
789
8
1224
1224
45 1
6
2
3
31452621712
3747715
Рис. 1.3. Кодовый формат для вещественных чисел (а) и пример его
использования (б)
7
Теперь кодовый формат той же длины (2 байта) обеспечивает пред
ставление и целых, и нецелых значений, т. е. представление конеч
ного подмножества бесконечного множества вещественных чисел.
Коды знака числа и знака порядка могут принимать по два кор
ректных значения, а код каждой десятичной цифры – десять значе
ний. Общее количество корректных кодов составляет
2 * 2 * 10 * 10 * 10 = 4000
значений. Их стало вдвое больше за счет того, что «полезная длина»
кода увеличилась на один бит (код знака порядка).
Конкретный код в этом кодовом формате позволяет представить
вещественные десятичные числа из следующего ряда:
–9.9*10+9, –9.8*10+9, ..., –0.2*10–9, –0.1*10–9, –0.0,
+0.0, +0.1*10–9, +0.2*10–9, ..., +9.8*10+9, +9.9*10+9
По сравнению с кодовым форматом для целых чисел из подразд. 1.2,
диапазон возможных значений существенно расширен: наибольшее
положительное значение составляет примерно 10 миллиардов вмес
то примерно одной тысячи. Обеспечено представление и очень боль
ших, и очень маленьких чисел.
Будем считать, что каждое значение кода представляет не един
ственное вещественное значение, а некоторое множество близких друг
к другу вещественных значений.
Например, значения
12.1, 12.2
будут представлены как
1.2 * 10 +1
т. е. приближенно. В то же время значение 12.0 будет представлено
точно.
Таким образом, новые возможности достигнуты, в том числе и за
счет замены точного представления значения приближенным пред
ставлением: с точностью до двух старших значащих цифр.
1.4. Нормализация
Вещественное число можно представить как значение с плаваю
щей точкой многими способами, подбирая различные значения по
рядка.
8
Например, вещественное число 0.934 можно представить в кодо
вом формате из подразд. 1.3 и как значение 0.9 * 10+0, и как значение
9.3 * 10–1 (в обоих представлениях в мантиссе сохранены две деся
тичные цифры). Оба представления – приближенные, но второй ва
риант – более точный, и потому – предпочтительный.
Таким образом, рациональное использование кодового формата
требует, чтобы старшая цифра мантиссы была бы отлична от нуля.
Мантисса, старшая цифра которой отлична от нуля, называется нор'
мализованной (normalized). Повторим: нормализация достигается
выбором соответствующего значения порядка.
Но значение порядка ограничено, и потому нормализация возмож
на не всегда. Например, кодовый формат из подразд. 1.3 не позволя
ет нормализовать значение 0.92 * 10–9 : нормализованное представ
ление 9.2 * 10–10 потребует непредставимого порядка (–10).
Мантисса, старшая цифра которой равна нулю, называется денор'
мализованной (denormalized).
1.5. Область применения данных с плавающей точкой
У каждого из кодовых форматов для чисел есть своя область при
менения. Рассмотрим примеры, используя кодовые форматы из под
разд. 1.2 и 1.3.
Пример 1: Пусть требуется сложить значения +123 и +456.
Используя кодовый формат для целых значений, получим точную
сумму: +579.
Кодовый формат с плавающей точкой не позволяет представить
слагаемые точно: вместо первого слагаемого получим (с округлени
ем) +1.2*10+2 и +4.6*10+2 – вместо второго. Сложение этих значе
ний дает результат +5.8*10+2 (т. е. +580), который только прибли
зительно совпадает с точным результатом.
Пример 2: Пусть требуется сложить значения +1.23 и +4.56.
Теперь уже кодовый формат для целых значений «пробуксовыва
ет»: слагаемое +1.23 превращается в +1, а слагаемое +4.56 – в +4
(дробная часть отбрасывается). Сложение вырабатывает результат
+5, довольно неточный.
Кодовый формат с плавающей точкой обеспечивает значения сла
гаемых +1.2*10+0 и +4.6*10+0 (с округлением), что позволяет выра
ботать сумму +5.8*10+0 (точная сумма равна +5.79). Это существен
но ближе к точному результату.
Подводя итог, можно сказать, что данные целого типа предназна
чены для проведения точных вычислений с целыми числами, а дан
9
ные с плавающей точкой – для проведения приближенных вычисле'
ний с вещественными числами, с сохранением старших значащих
цифр результата.
Конечно, кодовые форматы, которые рассмотрены в подразд. 1.2
и 1.3, следует назвать не особенно рациональными. В самом деле,
цепочка из 16 битов (из 2 байтов) обеспечивает представление 216 =
65536 возможных значений, а используется только незначительная
часть из них (2000 и 4000 значений, т. е. порядка 3–6 %).
Но эти кодовые форматы позволяют наглядно изложить основ
ные идеи кодирования целых и вещественных значений в памяти вы
числительных машин. Точные сведения о фактическом кодировании
значений с плавающей точкой в памяти IBM PC приведены в следую
щих разделах.
10
2. КОДОВЫЕ ФОРМАТЫ С ПЛАВАЮЩЕЙ ТОЧКОЙ
Кодовые форматы с плавающей точкой прошли долгий и слож
ный путь исторического развития и совершенствования. Удачные
технические решения, которые сегодня кажутся такими очевидны
ми, нашлись далеко не сразу.
В этом разделе подробно рассмотрены кодовые форматы, зафикси
рованные в стандарте IEEE 7541985 Института инженеров по элек
тротехнике и радиоэлектронике (Institute of Electrical and Electronics
Engineers, IEEE) [4]. Эти форматы используются в IBM PC и во мно
гих других вычислительных машинах [3].
В последующем изложении термин «кодовый формат» без даль
нейшего уточнения означает: «один из кодовых форматов стандарта
IEEE 7541985».
2.1. Структура кодового формата с плавающей точкой
Стандарт IEEE 754 специфицирует четыре кодовых формата дан
ных с плавающей точкой:
формат одинарной точности (Single);
расширенный формат одинарной точности (Single Extended);
формат двойной точности (Double);
расширенный формат двойной точности (Double Extended).
В процессорах фирмы Intel устройство с плавающей точкой
(Floating Point Unit, FPU) использует три кодовых формата:
формат одинарной точности (Single Precision, SP);
формат двойной точности (Double Precision, DP);
формат расширенной точности (Extended Precision, EP).
При этом соответствие требованиям стандарта IEEE 754 обеспече
но следующим образом:
формат одинарной точности (Single) стандарта IEEE 754 пред
ставлен форматом SP устройства FPU;
расширенный формат одинарной точности (Single Extended) –
форматом DP;
формат двойной точности (Double) – форматом DP;
11
расширенный формат двойной точности (Double Extended) – фор
матом EP.
Концептуально все эти форматы представляют вещественное зна
чение в форме пары
(мантисса, порядок)
Используется основание системы счисления (англ. base или radix),
равное 2, двоичная мантисса и двоичный код значения порядка:
значение = ( знакчисла ) * мантисса * 2 ( знакпорядка ) * порядок
Сокращенный пример представления значения +6.25 в такой фор
ме показан на рис. 2.1.
Более точно любой из кодовых форматов устройства FPU показан
на рис. 2.2.
а)
2
2
1
3
1
2
121314
161314
161314
121314
121314
б)
4
2
3
2
1
1
2
12
51
13
2
7
4
7181418
53
52
5
2
7
519
2
7
514
517
6
46
Рис. 2.1. Представление значения +6.25:
а – в двоичной системе счисления; б – в нормализованной форме
с плавающей точкой
1234
567
894
29
Рис. 2.2. Общая структура кодовых форматов устройства FPU:
Sign – код, представляющий знак числа; Exp – код, представляю'
щий значение и знак порядка; Mantissa – код, представляющий
значение (абсолютную величину) мантиссы
12
В кодовом формате элементы «пары» поменялись местами: поря
док хранится левее мантиссы. Для большинства людей это несуще
ственно (с деталями кодового формата работают немногие), а для
организации аппаратуры дает некоторые преимущества (подпод
разд. 2.1.3).
В памяти IBM PC отдельные байты форматов SP, DP, EP хранятся
в таком порядке, что логически старший байт хранится по старшему
адресу. Передача данных всегда начинается с младшего адреса (поря
док байтов подробно рассмотрен в разд. 4).
В разд. 2 кодовые форматы для удобства читателя представлены
в так называемом логическом порядке байтов: логически старший
байт занимает самую левую позицию (см. рис. 2.2).
2.1.1. Представление знака числа
Знак числа представлен в форматах SP, DP, EP самым старшим
(левым) битом кодового формата. Знак ‘+’ закодирован битом 0. Знак
‘–‘ закодирован битом 1.
2.1.2. Представление значения мантиссы
Устройство FPU обычно поддерживает представление мантиссы в
нормализованной форме:
M0 . M1 M2 . . . Mn
где Mk – отдельный бит мантиссы. Мантисса представлена в прямом
коде (по абсолютной величине).
Старший бит M0 нормализованной мантиссы равен единице. На
помним: основание используемой системы счисления равно 2.
Таким образом, нормализованная мантисса может принимать зна
чения в диапазоне 1.0 мантисса < 2.0. Отметим, что это представле
ние не позволяет представить число нуль, и для его кодирования ис
пользован специальный код.
Кроме того, устройство FPU может хранить и обрабатывать зна
чения с денормализованной мантиссой. Денормализованная мантис
са содержит нули в старших битах.
Этот и другие специальные случаи рассмотрены в подразд. 2.5.
2.1.3. Представление значения порядка
Порядок значения с плавающей точкой представлен в кодовом
формате в форме так называемого смещенного порядка (biased
exponent).
13
Более точно, форматы SP, DP, EP содержат поле смещенного по
рядка Exp (EXPonent), которое представляет истинный двоичный
порядок, к которому добавлено смещение (bias):
Exp ( истинныйпорядок ) + ( смещение )
Смещенный порядок Exp можно считать целым положительным
числом без знака.
Смещенный порядок из всех нулей (00...0) называется минималь'
ным смещенным порядком. Смещенный порядок из всех единиц
(11...1) называется максимальным смещенным порядком.
Минимальный и максимальный смещенные порядки зарезерви
рованы для представления специальных значений (этот вопрос об
суждается в подразд. 2.5). Все остальные значения смещенного по
рядка используются для кодирования нормализованных значений.
Наиболее очевидное преимущество смещенного порядка – отсут
ствие необходимости представления отрицательных значений поряд
ка.
Например, для формата SP значение смещения порядка равно 127,
а разрядность поля Exp составляет 8 битов. Таким образом, поле
смещенного порядка Exp для нормализованных значений может при
нимать значения в диапазоне [ 1, 254 ], что соответствует диапазону
значений истинного порядка [ –126, +127 ].
Менее очевидно, что задание порядка в форме со смещением
(и левее мантиссы) упрощает операцию сравнения чисел, превращая
ее в операцию сравнения целых чисел. В то же время выполнение
остальных операций практически не усложняется.
Операции над целыми числами выполняются значительно быст
рее операций над числами с плавающей точкой. Поэтому сокращение
времени выполнения операции сравнения может иметь существен
ное значение для алгоритмов с большим количеством сравнений (на
пример, для алгоритмов сортировки).
2.2. Кодовый формат одинарной точности (SP)
2.2.1. Организация формата SP
Длина кодового формата SP составляет 32 бита (4 байта). Обычно
требуется, чтобы адрес младшего из этих байтов был кратен четы
рем. Структурные части формата SP показаны на рис. 2.3.
При хранении чисел в формате SP в памяти старший бит M0 ман
тиссы в явном виде не хранится. Это так называемый скрытый (или
неявный) бит, который в нормализованных числах содержит 1. От
14
12345
623457
1234
567
8923457
82 81
813
12345678983
8 56
466 6
Рис. 2.3. Кодовый формат SP
метим, что скрытый бит (hidden bit) можно реализовать только в том
случае, когда основание, на степень которого умножается мантисса,
равно 2.
Как указано выше, значение поля смещенного порядка Exp опре
делено тождеством
Exp ( истинныйпорядок ) + ( смещение )
Для кодового формата SP смещение равно 127.
Таким образом, поле Exp нормализованных значений в формате
SP может принимать значения в диапазоне [ 1, 254 ], а истинный
порядок может изменяться в диапазоне [ –126, +127 ]. Напомним,
что минимальный смещенный порядок 0 и максимальный смещен
ный порядок 255 зарезервированы для представления специальных
значений (подразд. 2.5).
2.2.2. Примеры представления значений в формате SP
Рассмотрим простые примеры представления значений в формате
SP.
Значение: 0.5
Нормализованная двоичная мантисса:
1.00000000000000000000000
Истинный двоичный порядок: (–1)
Поле Exp:
126
Код:
0 01111110 00000000000000000000000
Значение: 1.0
Нормализованная двоичная мантисса:
1.00000000000000000000000
Истинный двоичный порядок: 0
Поле Exp:
127
Код:
0 01111111 00000000000000000000000
15
Значение: 1.5
Нормализованная двоичная мантисса:
1.10000000000000000000000
Истинный двоичный порядок: 0
Поле Exp:
127
Код:
0 01111111 10000000000000000000000
Значение: 2.0
Нормализованная двоичная мантисса:
1.00000000000000000000000
Истинный двоичный порядок: +1
Поле Exp:
128
Код:
0 10000000 00000000000000000000000
Максимальное положительное нормализованное значение
Нормализованная двоичная мантисса:
1.11111111111111111111111
Истинный двоичный порядок: +127
Поле Exp:
254
Код:
0 11111110 11111111111111111111111
(это значение приблизительно равно 3.40*10+38).
Минимальное положительное нормализованное значение
Нормализованная двоичная мантисса:
1.00000000000000000000000
Истинный двоичный порядок: (–126)
Поле Exp:
1
Код:
0 00000001 00000000000000000000000
(это значение приблизительно равно 1.17*10–38).
2.3. Кодовый формат двойной точности (DP)
2.3.1. Организация формата DP
Длина кодового формата DP составляет 64 бита (8 байтов). Обыч
но требуется, чтобы адрес младшего из этих байтов был кратен вось
ми. Структурные части формата DP показаны на рис. 2.4.
При хранении чисел в формате DP в памяти старший бит M0 ман
тиссы в явном виде не хранится (так же, как и в формате SP). Для
нормализованных значений бит M0 равен 1.
Как указано выше, значение поля смещенного порядка Exp опре
делено тождеством
Exp ( истинныйпорядок ) + ( смещение )
16
12345
1123456
1234
567
7823456
82 81
999
8 31
12345678983
8 56
466 6
Рис. 2.4. Кодовый формат DP
Для кодового формата DP смещение равно 1023.
Таким образом, поле Exp нормализованных значений в формате
DP может принимать значения в диапазоне [ 1, 2046 ], а истинный
порядок может изменяться в диапазоне [ –1022, +1023 ]. Напом
ним, что минимальный смещенный порядок 0 и максимальный сме
щенный порядок 2047 зарезервированы для представления специ
альных значений (подразд. 2.5).
2.3.2. Примеры представления значений в формате DP
Рассмотрим простые примеры представления значений в формате
DP.
Значение: 0.5
Нормализованная двоичная мантисса:
1.00000000000000000000 ... 0
Истинный двоичный порядок: (–1)
Поле Exp:
1022
Код:
0 01111111110 0000000000000000000000 ... 0
Значение: 1.0
Нормализованная двоичная мантисса:
1.00000000000000000000 ... 0
Истинный двоичный порядок: 0
Поле Exp:
1023
Код:
0 01111111111 0000000000000000000000 ... 0
Значение: 1.5
Нормализованная двоичная мантисса:
1.10000000000000000000 ... 0
Истинный двоичный порядок: 0
Поле Exp:
1023
Код:
0 01111111111 1000000000000000000000 ... 0
17
Значение: 2.0
Нормализованная двоичная мантисса:
1.00000000000000000000 ... 0
Истинный двоичный порядок: +1
Поле Exp:
1024
Код:
0 10000000000 0000000000000000000000 ... 0
Максимальное положительное нормализованное значение
Нормализованная двоичная мантисса:
1.11111111111111111111 ... 1
Истинный двоичный порядок: +1023
Поле Exp:
2046
Код:
0 11111111110 1111111111111111111111 ... 1
(это значение приблизительно равно 1.79*10+308).
Минимальное положительное нормализованное значение
Нормализованная двоичная мантисса:
1.00000000000000000000 ... 0
Истинный двоичный порядок: (–1022)
Поле Exp:
1
Код:
0 00000000001 0000000000000000000000 ... 0
(это значение приблизительно равно 2.22*10–308).
2.4. Кодовый формат расширенной точности (EP)
Длина кодового формата EP составляет 80 битов (10 байтов).
Структурные части этого формата показаны на рис. 2.5.
Числа в формате EP имеют явный старший бит M0 мантиссы. Та
кой формат позволяет несколько повысить скорость выполнения
операций и обеспечить некоторые преимущества благодаря простоте
представления чисел, которые не нормализованы.
12345
1623457
1234
567
12345
81 84 85
12345678983
8 56
466 6
Рис. 2.5. Кодовый формат EP
18
8923457
8 23
Как указано выше, значение поля смещенного порядка Exp опре
делено тождеством
Exp ( истинныйпорядок ) + ( смещение )
Для кодового формата EP смещение равно 16383.
Таким образом, поле Exp нормализованных значений в формате
EP может принимать значения в диапазоне [ 1, 32766 ], а истинный
порядок может изменяться в диапазоне [ –16382, +16383 ]. Напом
ним, что минимальный смещенный порядок 0 и максимальный сме
щенный порядок 32767 зарезервированы для представления специ
альных значений (подразд. 2.5).
Формат EP является единственным форматом представления чи
сел внутри устройства FPU. Числа в рассмотренных ранее форматах
SP и DP существуют только в памяти. При загрузке числа в одном из
этих форматов в регистр устройства FPU, оно автоматически преоб
разуется в 80битовый формат EP, который используется при вы
полнении операций. При сохранении результатов операций в памя
ти в форматах SP и DP выполняется обратное преобразование.
Числа в формате EP также можно сохранять в памяти. Обычно
это требуется при запоминании промежуточных результатов, кото
рые не удается сохранить в регистрах устройства FPU.
2.5. Специальные значения
При выполнении вычислений с плавающей точкой могут возни
кать так называемые исключительные ситуации (exceptions). Стан
дарт IEEE 754 определяет следующие 5 классов исключительных
ситуаций.
Underflow (исчезание): в ходе вычислений получено слишком
маленькое (по абсолютной величине) значение, представить которое
не позволяет ограниченный размер поля Exp.
Overflow (переполнение): в ходе вычислений получено слишком
большое (по абсолютной величине) значение, представить которое
не позволяет ограниченный размер поля Exp.
Zerodivide (деление на нуль): в ходе вычислений потребовалось
выполнить деление на нуль.
Invalid operation (недействительная операция): в ходе вычисле
ний потребовалось получить результат незаконной операции (напри
мер, извлечь квадратный корень из отрицательного значения).
Inexact (потеря точности): в ходе вычислений получено значе
ние, представить которое точно не позволяет ограниченный размер
поля Mantissa.
19
Как же следует поступать в случае возникновения одной из пере
численных исключительных ситуаций?
Традиционно необходимость вычисления результата такой опе
рации как ( 0.0 / 0.0 ) или ( 1) трактовалась как неисправимая
ошибка, возникновение которой приводило к выводу сообщения об
ошибке и последующему прекращению вычислений. Однако можно
привести немало примеров вычислительных алгоритмов, для кото
рых имеет смысл продолжить вычисления после возникновения од
ной из исключительных ситуаций [5].
Поэтому стандарт IEEE 754 предусматривает так называемые спе'
циальные значения (special values), которые предназначены для пред
ставления результата вычислений в подобных случаях.
С корректным проведением вычислений с плавающей точкой свя
зано немало тонкостей. Их детальное рассмотрение выходит за рам
ки этой книги. Полное и всестороннее изложение вопроса можно най
ти в статье [5] и цитированной в ней литературе.
2.5.1. Денормализованные значения
Когда при вычислениях значение с плавающей точкой приближа
ется к нулю, порядок значения может оказаться слишком отрица
тельным. Например, для формата DP значение истинного порядка
может оказаться меньше, чем его предельное отрицательное значе
ние (–1022).
Для обработки таких ситуаций устройство FPU может хранить и
обрабатывать вещественные числа, которые не являются нормали
зованными, т. е. числа с нулевыми старшими битами мантиссы. Та
ким образом, за счет некоторой потери точности обеспечивается воз
можность работы с числами, которые меньше минимального норма
лизованного значения.
В связи с денормализованными значениями использование скры
того бита (hidden bit) для представления старшего бита M0 мантиссы
порождает небольшую проблему. Рассмотрим ее на примере формата
SP.
Вот минимальное положительное нормализованное значение в
формате SP (скрытый бит и двоичная точка показаны в явном виде):
Полный код:
0 00000001 1.00000000000000000000000
Код SP:
0 00000001 00000000000000000000000
Интерпретация:
1.000000000000000000000002 * 2–126
Следующее (в сторону уменьшения) значение должно быть уже
денормализованным:
20
Полный код:
0 00000001 0.11111111111111111111111
Код SP:
0 00000001 11111111111111111111111
Ожидаемая интерпретация: 0.111111111111111111111112 * 2–126
К сожалению, фактическая интерпретация не совпадает с наши
ми ожиданиями, поскольку предполагается, что скрытый бит M0
мантиссы всегда равен единице:
Полный код:
0 00000001 1.11111111111111111111111
Код SP:
0 00000001 11111111111111111111111
Фактическая интерпретация: 1.111111111111111111111112 * 2–126
Как видно из последних двух примеров, различные значения с
плавающей точкой оказываются представленными одним и тем же
кодом.
Эта проблема решена следующим образом. В коде денормализо
ванных значений поле смещенного порядка Exp состоит из всех ну
лей, а при интерпретации кода используется смещение 126 (а не 127,
как для нормализованных значений).
Вот пример интерпретации кода максимального положительного
денормализованного значения в формате SP:
Полный код:
0 00000000 0.11111111111111111111111
Код SP:
0 00000000 11111111111111111111111
Фактическая интерпретация: 0.111111111111111111111112 * 2–126
Итак, денормализованные числа имеют минимальный смещенный
порядок (из всех нулей), а старший бит M0 мантиссы (явный или
неявный) равен нулю.
Минимальное положительное денормализованное значение
в формате SP
Двоичная мантисса:
0.00000000000000000000001
Истинный двоичный порядок: (–126)
Поле Exp:
0
Код:
0 00000000 00000000000000000000001
(это значение приблизительно равно 1.40*10–45).
Если же значение еще меньше, то создается исключительная си
туация, которая называется исчезанием (underflow). Логично счи
тать, что в результате исчезания получается значение нуль.
2.5.2. Бесконечность
Ситуация, обратная исчезанию, складывается тогда, когда ре
зультат вычислений становится слишком большим, так что ограни
ченный размер поля Exp не позволяет представить слишком боль
шой порядок этого результата.
21
Например, для формата DP значение истинного порядка может
оказаться больше, чем его предельное положительное значение
+1023. Такая ситуация называется переполнением (overflow).
Для обработки переполнения форматы SP, DP и EP поддержива
ют знаковое представление бесконечности, т. е. значения «плюс бес
конечность» и «минус бесконечность». Эти значения кодируются
максимальным смещенным порядком (из всех единиц) и мантиссой
1.000 ... 0.
Значение «+ » в формате DP
Двоичная мантисса:
1.00000000000000000000 ... 0
Поле Exp:
2047
Код:
0 11111111111 0000000000000000000000 ... 0
Значение «– » в формате DP
Двоичная мантисса:
1.00000000000000000000 ... 0
Поле Exp:
2047
Код:
1 11111111111 0000000000000000000000 ... 0
Знаки бесконечностей учитываются и сравнения возможны.
Бесконечность всегда интерпретируется в аффинном смысле, т. е.
( любоеконечноечисло )
Отметим, что результат «бесконечность» может появиться
вследствие разных обстоятельств. Например, результат операции
( 1.0 / 0.0 ) равен + по определению (подлинная бесконечность).
В то же время результат операции ( 1.0 * 10 +30 ) * ( 1.0 * 10 +30 )
в формате SP приводит к переполнению и также должен быть пред
ставлен значением + .
2.5.3. Нуль
В категорию специальных значений попадает и значение «нуль» в
результате невозможности его представления с использованием нор
мализованной мантиссы.
Значение «нуль» в форматах SP, DP, EP кодируется минималь
ным смещенным порядком (из всех нулей) и нулевой мантиссой. При
этом знаковый бит может содержать либо 0, либо 1, т. е. значение
«нуль» является знаковым.
Значение «+0.0» в формате SP
Двоичная мантисса:
0.00000000000000000000000
Поле Exp:
0
Код:
0 00000000 00000000000000000000000
22
Значение «–0.0» в формате SP
Двоичная мантисса:
0. 00000000000000000000000
Поле Exp:
0
Код:
1 00000000 00000000000000000000000
При сравнении значения +0.0 и –0.0 считаются совпадающими.
Вычисление выражения ( +0.0 == –0.0 ) вырабатывает результат ис'
тина, а вычисление выражения ( +0.0 > –0.0 ) вырабатывает резуль
тат ложь.
Очевидно, этот особый случай (как и любой подобный) связан с
накладными расходами. Однако вопрос о том, должно ли значение
«нуль» иметь знак, совсем не праздный.
Рассмотрим пример: вычисление математической функции log (ло
гарифм), которая определена для аргументов x 0. Предположим,
что аргумент x – небольшое положительное число, которое в резуль
тате исчезания представлено значением +0.0. Естественно специфи
цировать, что
log ( +0.0 )
Если же x – небольшое отрицательное число, которое в результате
исчезания представлено значением –0.0, столь же естественно спе
цифицировать, что
log ( –0.0 ) = нечисло.
2.5.4. Нечисла
Нечисла (NotaNumbers, NaNs) являются представителями клас
са специальных значений данных в форматах SP, DP, EP. Они имеют
максимальный смещенный порядок (из всех единиц), любой знак и
любую мантиссу, за исключением 1.00 ... 00 (такая мантисса отведе
на для представления бесконечностей).
Имеются два подкласса нечисел: сигнализирующие нечисла
(Signaling NaNs) и тихие нечисла (Quiet NaNs).
Сигнализирующие нечисла зарезервированы для представления
значений операндов, использование которых сигнализирует о выпол
нении одной из недействительных операций (invalid operation).
Тихие нечисла, по определению, оставлены на усмотрение реали
зации. Они обеспечивают возможность сохранения диагностической
информации о некорректных операндах или о некорректных резуль
татах некоторой операции.
23
2.5.5. Сигнализирующие нечисла (Signaling NotCaCNumbers)
Сигнализирующее нечисло имеет единицу в бите M0 мантиссы (яв
ном или неявном) и нуль в бите M1 мантиссы. Остальные биты ман
тиссы могут принимать любые значения. Исключение составляет
мантисса
1.00000 ... 00
(такая мантисса отведена для представления бесконечностей).
Минимальное положительное Signaling NaN в формате DP
Двоичная мантисса:
1.00000000000000000000 ... 01
Поле Exp:
2047
Код:
0 11111111111 0000000000000000000000 ... 01
Максимальное положительное Signaling NaN в формате DP
Двоичная мантисса:
1.01111111111111111111 ... 11
Поле Exp:
2047
Код:
0 11111111111 0111111111111111111111 ... 11
Устройство FPU никогда не формирует сигнализирующее нечисло
как результат операции, но распознает сигнализирующие нечисла,
когда они служат операндами. Арифметические операции с нечисла
ми обычно приводят к возникновению особого случая недействитель
ной операции.
Разрешая особый случай недействительной операции, програм
мист может использовать сигнализирующие нечисла для вызова об
работчика особого случая. Общность такого подхода и большое ко
личество возможных значений нечисел предоставляют опытному
программисту средство, которое может оказаться полезным в раз
личных специальных ситуациях.
Например, разработчик компилятора может использовать сигна
лизирующие нечисла для неявной инициализации элементов веще
ственного массива, для которых явная инициализация не задана.
Компилятор может поместить в каждый такой элемент массива сиг
нализирующее нечисло, мантисса которого содержит индекс элемен
та массива.
Если прикладная программа обратится к такому элементу, то воз
никнет особый случай недействительной операции. Если особый слу
чай разрешен, то возникнет прерывание и будет вызван обработчик
особого случая.
Обработчик сможет определить, к какому элементу массива про
исходило обращение: поле адреса операнда обработчика особого слу
24
чая показывает на нечисло, а мантисса нечисла содержит индекс эле
мента массива.
2.5.6. Тихие нечисла (Quiet NotCaCNumbers)
Тихое нечисло имеет единицу в бите M0 мантиссы (явном или не
явном) и единицу в бите M1 мантиссы. Остальные биты мантиссы
могут принимать любые значения.
Минимальное положительное Quiet NaN в формате DP
Двоичная мантисса:
1.10000000000000000000 ... 0
Поле Exp:
2047
Код:
0 11111111111 1000000000000000000000 ... 0
Максимальное положительное Quiet NaN в формате DP
Двоичная мантисса:
1.11111111111111111111 ... 1
Поле Exp:
2047
Код:
0 11111111111 1111111111111111111111 ... 1
Устройство FPU формирует особое тихое нечисло, которое назы
вается вещественной неопределенностью (подподразд. 2.5.7), как
реакцию по умолчанию на некоторые особые условия.
Устройство FPU может также формировать другие тихие нечис
ла, преобразуя сигнализирующие нечисла. При этом в бит M1 ман
тиссы помещается единица, а остальные биты мантиссы не изме
няются. Следовательно, если мантисса сигнализирующего числа
содержала диагностическую информацию, то эта информация со
храняется.
Тихие нечисла можно использовать, например, для ускорения
отладки. На ранних этапах отладки в программе часто есть не
сколько ошибок, которые приводят к возникновению особого слу
чая. При вызове обработчик особого случая может сохранить в
памяти диагностическую информацию, а в качестве результата
некорректной операции подставить тихое нечисло, которое содер
жит указатель на сохраненную диагностическую информацию. В
результате за один прогон отлаживаемой программы можно обна
ружить много ошибок.
Во встроенных системах, когда вычисленные результаты участву
ют в дальнейших расчетах, необнаруженные тихие нечисла могут
привести к недостоверности всех последующих результатов. Следует
периодически контролировать появление тихих нечисел и предусмот
реть механизм исправления ошибок для случая, когда тихие нечис
ла появляются.
25
2.5.7. Неопределенность
Если в процессе вычислений с плавающей точкой возникает за
маскированный особый случай недействительной операции, то уст
ройство FPU формирует в качестве результата специальное тихое не
число, которое называется вещественной неопределенностью
(indefiniteness).
Неопределенность предусмотрена для вычислительных ситуаций,
в которых человек говорит «не знаю». Типичным примером такой
ситуации является деление нуля на нуль.
Неопределенность представлена тихим нечислом, которое имеет
знак «минус», а мантисса равна
1.10000 ... 00
Неопределенность INDefiniteness в формате DP
Двоичная мантисса:
1.1000000000000000000000 ... 0
Поле Exp:
2047
Код:
1 11111111111 1000000000000000000000 ... 0
2.6. Сводная таблица кодирования данных
в форматах SP и DP
Кодирование данных в форматах SP и DP приведено в табл. 2.1.
Таблица 2.1
Kласс значения
Знак
Exp
Мантисса
Положительные значения
Нечисла
Тихие нечисла
Сигнализирующие
0
11 ... 11
11 ... 11
...
...
0
11 ... 11
10 ... 00
0
11 ... 11
01 ... 11
...
...
0
11 ... 11
00 ... 01
0
11 ... 11
00 ... 00
нечисла
+¥
26
Окончание табл. 2.1
Kласс значения
Знак
Exp
Мантисса
Вещественные числа
Нормализованные
Денормализованные
+ 0.0
0
11 ... 10
11 ... 11
...
...
0
00 ... 01
00 ... 00
0
00 ... 00
11 ... 11
...
...
0
00 ... 00
00 ... 01
0
00 ... 00
00 ... 00
Отрицательные значения
–0.0
1
00 ... 00
00 ... 00
Вещественные числа
Денормализованные
00 ... 00
00 ... 01
...
...
1
00 ... 00
11 ... 11
1
00 ... 01
00 ... 00
...
...
1
11 ... 10
11 ... 11
1
11 ... 11
00 ... 00
11 ... 11
00 ... 01
...
...
1
11 ... 11
01 ... 11
Неопределенность
1
11 ... 11
10 ... 00
Другие тихие нечисла
1
11 ... 11
10 ... 01
...
...
11 ... 11
11 ... 11
Нормализованные
–¥
1
Нечисла
Сигнализирующие
1
нечисла
1
27
2.7. Сводная таблица кодирования данных в формате EP
Кодирование данных в формате EP показано в табл. 2.2 . Напом
ним, что в формате EP бит M0 мантиссы хранится в явном виде.
Таблица 2.2
Kласс значения
Знак
Exp
Мантисса
Положительные значения
Нечисла
Тихие нечисла
Сигнализирующие
0
11 ... 11
1 . 11 ... 11
...
...
0
11 ... 11
1 . 10 ... 00
0
11 ... 11
1 . 01 ... 11
...
...
0
11 ... 11
1 . 00 ... 01
0
11 ... 11
1 . 00 ... 00
нечисла
+¥
Вещественные числа
Нормализованные
Денормализованные
+ 0.0
0
11 ... 10
1 . 11 ... 11
...
...
0
00 ... 01
1 . 00 ... 00
0
00 ... 00
0 . 11 ... 11
...
...
0
00 ... 00
0 . 00 ... 01
0
00 ... 00
0 . 00 ... 00
Отрицательные значения
– 0.0
1
00 ... 00
0 . 00 ... 00
Вещественные числа
Денормализованные
1
1
28
00 ... 00
0 . 00 ... 01
...
...
00 ... 00
0 . 11 ... 11
Окончание табл. 2.2
Kласс значения
Нормализованные
-¥
Знак
Exp
Мантисса
1
00 ... 01
1 . 00 ... 00
...
...
1
11 ... 10
1 . 11 ... 11
1
11 ... 11
1 . 00 ... 00
Нечисла
Сигнализирующие
1
11 ... 11
1 . 00 ... 01
...
...
1
11 ... 11
1 . 01 ... 11
Неопределенность
1
11 ... 11
1 . 10 ... 00
Другие тихие нечисла
1
11 ... 11
1 . 10 ... 01
...
...
11 ... 11
1 . 11 ... 11
нечисла
1
2.8. Неподдерживаемые коды в формате EP
Формат EP имеет множество значений кода, которые не попада
ют ни в один из классов значений, рассмотренных ранее. Эти значе
ния кода называются неподдерживаемыми кодами.
Неподдерживаемые коды показаны в табл. 2.3.
Таблица 2.3
Kласс значения
Знак
Exp
Мантисса
Положительные значения
Псевдонечисла
Тихие псевдонечисла
0
0
11 ... 11
0 . 11 ... 11
...
...
11 ... 11
0 . 10 ... 00
29
Окончание табл. 2.2
Kласс значения
Сигнализирующие
Знак
Exp
Мантисса
0
11 ... 11
0 . 01 ... 11
...
...
0
11 ... 11
0 . 00 ... 01
0
11 ... 11
0 . 00 ... 00
псевдонечисла
Псевдо +¥
Вещественные числа
Ненормализованные
Псевдо
0
11 ... 10
0 . 11 ... 11
...
...
0
00 ... 01
0 . 00 ... 00
0
00 ... 00
1 . 11 ... 11
...
...
00 ... 00
1 . 00 ... 00
денормализованные
0
Отрицательные значения
Вещественные числа
Псевдо
1
00 ... 00
1 . 00 ... 00
...
...
1
00 ... 00
1 . 11 ... 11
1
00 ... 01
0 . 00 ... 00
...
...
1
11 ... 10
0 . 11 ... 11
1
11 ... 11
0 . 00 ... 00
11 ... 11
0 . 00 ... 01
...
...
1
11 ... 11
0 . 01 ... 11
1
11 ... 11
0 . 10 ... 00
...
...
11 ... 11
0 . 11 ... 11
денормализованные
Ненормализованные
Псевдо –¥
Псевдонечисла
Сигнализирующие
1
псевдонечисла
Тихие псевдонечисла
1
Псевдонечисла, псевдобесконечности и ненормализованные чис
ла, которые существовали ранее, теперь не поддерживаются. Когда
30
они встречаются как операнды, устройство FPU генерирует особый
случай недействительной операции.
Псевдоденормализованные числа, которые существовали ранее,
теперь поддерживаются частично. Устройство FPU не образует зна
чений, которые ранее назывались псевдоденормализованными чис
лами. Однако, если такие значения встречаются как операнды, то
они обрабатываются правильно. При этом порядок считается рав
ным 00 ... 01, а мантисса не изменяется. Формируется особый слу
чай денормализованного операнда.
31
3. ДАННЫЕ С ПЛАВАЮЩЕЙ ТОЧКОЙ В ЯЗЫКАХ
ПРОГРАММИРОВАНИЯ ВЫСОКОГО УРОВНЯ
В связи с данными с плавающей точкой современные языки про
граммирования третьего поколения (Third Generation Languages,
3GL) используют практически одну и ту же устоявшуюся систему
обозначений (с небольшими вариациями). Эти обозначения и рас
смотрены ниже.
3.1. Переменные с плавающей точкой
Тип переменных с плавающей точкой во многих современных язы
ках программирования (C, C++, Java, C#, etc. [2, 9, 6, 1]) описыва
ют с использованием одних и тех же служебных слов: спецификато
ров типа float и double.
Тип float означает, что переменная принимает значения, которые
представлены в кодовом формате SP, и занимает 4 байта памяти
(рис. 3.1, а).
Тип double означает, что переменная принимает значения, кото
рые представлены в кодовом формате DP, и занимает 8 байтов памя
ти (рис. 3.1, б).
Кроме того, некоторые языки поддерживают тип long double. Этот
тип может означать, что переменная принимает значения, которые
представлены в кодовом формате EP, и занимает 10 байтов памяти.
а)
123
11123456
1
б)
8312342 7589242 96
72
11174893
7
43123456 7589242 56
Рис. 3.1. Описания переменных с плавающей точкой и хранение их
значений в памяти
32
Правда, может оказаться, что тип long double обеспечен кодовым
форматом DP и, тем самым, эквивалентен типу double. Это следует
уточнить по спецификации входного языка конкретного компиля
тора.
3.2. Литералы с плавающей точкой
Литералы – это слова, которые используют в тексте программы
для представления постянных значений (англ. a literal – букваль
ный).
Напомним: слово – это цепочка из одного или более соседних
символов текста на некотором языке. Правила составления слов
из соседних символов текста называются лексическими правила
ми языка.
3.2.1. Синтаксические диаграммы
Для однозначного описания правил записи текстов на языке про
граммирования (синтаксических правил) требуется подходящая фор
ма представления. Наиболее наглядная форма – синтаксические ди
аграммы, которые предложены швейцарским ученым Никлаусом
Виртом (Niklaus Wirth).
Синтаксические диаграммы составляют из элементов, которые
показаны на рис. 3.2.
Простейший пример синтаксической диаграммы показан на рис. 3.3.
123 1415675228
123245 672892
51 672892
7
1672892
455
9
58541723747 2692 45
15275112 9
112
342 12 698 672892
4
524 23747 98 45
15275112 51212
32415 5617 6 45274
9425
5671 4
1 672892
9
5
596
Рис. 3.2. Элементы синтаксических диаграмм
33
12345678941
69
1
2
3
4
5
6
7
8
9
Рис. 3.3. Синтаксическая диаграмма «десятичная цифра»
Дефис в «имени диаграммы» употреблен для формальной коррект
ности: в его отсутствие можно было бы думать, что речь идет о двух
понятиях языка: «десятичная» и «цифра».
3.2.2. Общие правила записи литералов с плавающей точкой
Правила записи литералов с плавающей точкой, конечно же, ори
ентированы на удобство восприятия текста человеком. Использует
ся десятичная система счисления, привычные значки ‘+’, ‘–’, etc.
Правда, в отличие от обозначений, принятых в арифметике, целую и
дробную части значения разделяет не десятичная запятая, а деся
тичная точка.
Рассмотрим семейство синтаксических диаграмм, которые пред
ставляют правила записи литералов, общие для языков C++, Java,
C# и многих других. Частные правила одного из этих языков могут
содержать незначительные вариации (это будет отмечено в ходе даль
нейшего изложения).
Самая общая диаграмма для понятия «литерал с плавающей точ
кой» показана на рис. 3.4.
Если «литерал с плавающей точкой» имеет суффикс ’F’ или суф
фикс ‘f’ (от слова float), то в результате преобразования литерала в
кодовый формат формируется код в формате SP (float). Если же суф
фикс отсутствует, то в результате преобразования формируется код
в формате DP (double).
1234561 17 181696
4 13
1234561 14 1856
1
123456117 185
2
Рис. 3.4. Синтаксическая диаграмма «литерал с плавающей точкой»
34
3.2.3. Литералы без порядка
Литералы без порядка удобно использовать для представления
таких значений, в которых значащие цифры находятся сравнитель
но недалеко от десятичной точки, разделяющей целую и дробную ча
сти значения. На рис. 3.5 показаны синтаксические диаграммы для
понятия «литерал без порядка».
Литералы с плавающей точкой всегда специфицируют положи
тельные значения. Если требуется отрицательное значение, то необ
ходимо сформировать константное выражение из знака «минус» и
следующего за ним литерала. При этом знак «минус» рассматривает
ся как одноместная арифметическая операция.
Вот примеры литералов без порядка:
6.25 772.08 0.002
Очевидно, непременный элемент «литерала без порядка» – деся
тичная точка: она отличает такой литерал от литерала целого типа.
Например, литерал 20 задает значение целого типа, а литерал 20. –
значение с плавающей точкой.
В последнем случае формально корректная запись литерала
20.
1234561 1748 1 9
56
416 1 63
1
5
76 163
1
5
76 163
416 163
4326 1 256
5
76 1 63
4326 1 256
Рис. 3.5. Синтаксические диаграммы для понятия «литерал без
порядка»
35
выглядит несколько необычно (для неискушенного читателя). По
этому лучше использовать такую форму записи, в которой десятич
ная точка употреблена в середине литерала без порядка. Например:
20.0 (а не: 20.) или 0.01 (а не: .01), etc.
Повторим: если требуется отрицательное значение, то перед лите
ралом нужно поставить знак «минус». Этот знак не является частью
литерала, и потому его можно писать и слитно с литералом, и раз
дельно:
–6.25 – 772.08
Символы, которые входят в состав литерала, необходимо записы
вать слитно, без промежуточных пробелов.
Обратите внимание: литералы – это не постоянные значения как
таковые, а их символическое представление в тексте программы. Соб
ственно постоянные значения с плавающей точкой – это цепочки
битов, которые составлены в соответствии с требованиями одного из
кодовых форматов (рис. 3.6).
В верхней части показан фрагмент текста программы: литерал с
плавающей точкой 1.0F (для полной ясности литерал отделен от со
седних слов текста пробелами). Каждый символ текста заключен в
прямоугольник. В нижней части прямоугольника помещено графи
ческое изображение символа (символ «пробел» представлен услов
ным знаком, который в просторечии называется «корытом»). В вер
хней части прямоугольника показан двоичный код ASCII каждого
символа.
В нижней части показан 4байтовый код SP (в логическом поряд
ке байтов), который получен в результате преобразования (переко
334323333 334423334 3343 4443 334423333 3433 3443 334323333
1
2
3
567816298
27 5678
4
9
7
322344444442233333333333333333333333
1212342
Рис. 3.6. Литерал без порядка и код соответствующего значения
36
дирования) литерала. Для наглядности поля Sign, Exp, Mantissa кода
разделены пробелами. Конечно же, в памяти вычислительной ма
шины все биты кода записаны подряд, без всяких разделителей.
3.2.4. Литералы с порядком
Литералы с порядком удобно использовать для записи таких зна
чений, в которых значащие цифры находятся на значительном уда
лении от десятичной точки.
При записи таких значений в форме литерала без порядка легко
ошибиться в количестве лидирующих (или завершающих) нулей.
Вот примеры подобных значений:
0.00000123
456000000.0
Практичнее представить эти значения в форме 1.23*10–6 и
4.56*10+8. Такую форму представления и обеспечивают литералы с
порядком (рис. 3.7).
Литера ‘e’ (или ‘E’) в литерале с порядком происходит от слова
«Exponent» и означает: «десять в степени».
Таким образом, приведенные выше примеры значений с плаваю
щей точкой практичнее записать в форме литералов с порядком:
1.23e–6
4.56e+8
6529 13 141
9856339
2
41
3
9856339
294 17935
1
1894 17935
1
1894 17935
41
3
1234567894 1
69
2
Рис. 3.7. Синтаксические диаграммы для понятия «литерал с порядком»
37
Следить за «нормализацией» мантиссы в литерале с порядком не
обязательно, например:
12.3e–7
456e6
Как видно из синтаксических диаграмм (см. рис. 3.7), в мантиссе
«литерала с порядком» можно не употреблять десятичную точку –
наличие порядка уже показывает, что речь идет о литерале с плаваю
щей точкой (а не о литерале целого типа).
Напомним: если требуется отрицательное значение, то перед ли
тералом нужно поставить знак «минус». Этот знак не является час
тью литерала, и потому его можно писать и слитно с литералом, и
раздельно:
–12.3e–7
–456e6
Символы, которые входят в состав литерала, необходимо записы
вать слитно, без промежуточных пробелов.
3.2.5. Литералы с плавающей точкой
как элементы исходных данных
Синтаксические диаграммы, которые рассмотрены в подпод
разд. 3.2.2–3.2.4, относятся к правилам записи текста программ.
Если же речь идет о правилах подготовки исходных данных, то
правила слегка меняются: при необходимости записи отрицательно
го значения знак «минус» и литерал с плавающей точкой должны
быть записаны слитно, без разделяющих пробелов.
Кроме того, может оказаться, что употребление суффикса ‘F’ (или
‘f’) в записи исходных данных не допускается.
3.2.6. Заключительные замечания
Оценить в полной мере наглядность и точность синтаксических
диаграмм позволяет их сравнение с текстовым изложением того же
самого содержания. Вот пример такого текста:
«Литерал с плавающей точкой – это действительное десятичное
положительное число. Оно включает целую часть, дробную часть и
экспоненту. Либо целая, либо дробная часть может быть опущена,
но не обе сразу. Либо десятичная точка с дробной частью, либо экспо
нента может быть опущена, но не обе сразу» [2].
Etc, etc. Как говорится, комментарии излишни.
Конечно, как и всякое изобразительное средство, синтаксические
диаграммы позволяют дать ясное и точное представление правил за
писи текста только при условии надлежащего использования. В кни
38
гах по программированию можно встретить немало запутанных син
таксических диаграмм, которые совершенно ненаглядны (и нередко
содержат ошибки).
Отметим, что наличие суффикса ‘F’ (или ‘f’) в записи литерала
однозначно показывает, что речь идет о литерале с плавающей точ
кой. Поэтому язык программирования, который предусматривает
употребление подобных суффиксов, может также допускать, что при
наличии суффикса литерал без порядка не обязан содержать деся
тичную точку. Например: 1F (суффикс ‘F’ показывает, что речь идет
о значении с плавающей точкой 1.0, а не о целом значении 1).
Но некоторые языки (например, ранние версии языка Си) не пре
дусматривают употребления таких суффиксов. Поэтому для удобства
читателячеловека рекомендуется рассматривать как определяющий
признак литерала с плавающей точкой наличие в записи литерала
либо десятичной точки, либо порядка. Это «сработает» всегда.
Суффикс можно считать дополнительной информацией.
Составление (генерация) кода значения в формате DP (или SP) на
основе литерала с плавающей точкой – сравнительно сложная зада
ча. Ее решение подробно рассмотрено в работе [4].
39
4. ПРОГРАММА ДЛЯ ИЗУЧЕНИЯ КОДОВЫХ ФОРМАТОВ
С ПЛАВАЮЩЕЙ ТОЧКОЙ
Сведения из справочной литературы, которые изложены в разд. 2,
полезно подкрепить «натурным экспериментом». В этом разделе рас
смотрена программа на языке С/С++, которая позволяет задавать
значения с плавающей точкой не только в форме литералов с плава
ющей точкой, но и в форме цепочек битов. Последняя форма задания
обеспечивает возможность работы со специальными значениями (под
разд. 2.5).
Программа должна демонстрировать любое значение с плавающей
точкой как в форме литерала с порядком, так и в форме цепочки би
тов.
4.1. Двоичные и символические байты
Работая с кодовым форматом как с цепочкой битов, уместно опе
рировать понятиями «бит» и «байт». В целях создания возможно
более ясного текста программы желательно, чтобы в нем были ис
пользованы термины «Bit» и «Byte».
В программах на языке С/С++ байту памяти соответствует тип
данных unsigned char. Кстати, заметим, что спецификатор unsigned
правильнее интерпретировать словами «цепочка битов», а не слова
ми «целое число без знака».
Для того чтобы рассматривать некоторую область памяти (Random
Access Memory, RAM) и как целостный кодовый формат, и как от
дельные байты, лучше всего воспользоваться объединением (union).
Например, для работы с кодовым форматом SP целесообразно оп
ределить тип TFloatCode и экземпляр BinFloat этого типа:
union TFloatCode
{
float
unsigned char
);
static TFloatCode
40
// Type FLOAT CODE
Value;
Byte[ 4 ];
// single precision value
// the same as separate bytes
BinFloat;
// BINary representation
// of a FLOAT item
Тип объединения TFloatCode содержат два варианта. Вариант
Value соответствует кодовому формату SP. Вариантмассив Byte [4]
трактует ту же самую область памяти как отдельные байты.
Его использование иллюстрирует рис. 4.1, где экземпляр BinFloat
объединения типа TFloatCode занимает байты памяти с адресами
200–203. Адреса байтов увеличиваются слева направо, что соответ
ствует привычной для человека ориентации числовой оси. Байтам
200–203 соответствует и вариант Value в целом, и отдельные элемен
ты вариантамассива Byte [4].
В разд. 2 уже упоминалось, что отдельные байты форматов SP,
DP, EP хранятся в памяти в таком порядке, что младший байт зани
мает младший адрес. Рассмотрим этот вопрос детально на примере
формата SP (рис. 4.2).
BinFloat
455
Value
456
454
457
5
6
4
1234567
384793 123
7
Byte[ 4 ]
Рис. 4.1. Размещение в памяти экземпляра BinFloat объединения
(union) типа TFloatCode
BinFloat
1234567
384793 123
Byte[ 4 ]
456
454
457
455
5577887777 7555885555 5555885555 5555885555
123 9
45673
Value
3
58885777777788855555555555555555555555
Рис. 4.2. Физическое размещение байтов в памяти
41
Здесь адреса байтов памяти увеличиваются справа налево. В ре
зультате старшие байты значения изображены слева, а младшие бай
ты – справа. Это соответствует привычной для человека форме запи
си цифр некоторого числа: слева направо, от старших цифр к млад
шим.
Отдельные части кодового формата SP, который используется для
кодирования варианта Value, показаны без соблюдения масштаба для
того, чтобы в прямоугольники уместились по ширине названия по
лей. В качестве примера значения использован код литерала 1.0F
(см. подподразд. 2.2.2).
Для работы с кодовым форматом DP определим тип объединения
TDoubleCode и экземпляр BinDouble этого типа:
union TDoubleCode
{
double
unsigned char
);
static TDoubleCode
// Type DOUBLE CODE
Value;
Byte[ 8 ];
// double precision value
// the same as separate bytes
BinDouble;
// BINary representation
// of a DOUBLE item
Организация данных этого объединения аналогична той, которая
показана на рис. 4.1, 4.2. Изменился только размер – он стал равен
восьми байтам вместо четырех.
Обеспечивая визуальное наблюдение кодового формата, цепочку
битов необходимо представить в форме цепочки символов ‘0’ и/или
‘1’. В этой связи определим следующие понятия.
Символический бит – это символ, который может принимать толь
ко значения ‘0’ или ‘1’. Символический бит представлен элементом
данных типа char языка С/С++.
Символический байт – это цепочка из восьми символических би
тов, которая составляет часть значения корректной Сстроки (т. е.
цепочки символов, которая заканчивается нульсимволом ‘\0’). Пред
ставим символический байт нестандартным структурным типом:
struct TSymByt
{
char
Bit[ 8 ];
// Type SYMbolic BYTE:
// symbolic representation
// of a binary byte
// symbolic bits
);
Отдельные символические биты символического байта проиндек
сированы слева направо от 0 до 7, от старших битов к младшим. Это
не соответствует общепринятому порядку нумерации битов двоично
го кода (когда биты пронумерованы от младших битов к старшим).
42
Но изменить принятый в языке С/С++ порядок индексации элемен
тов массива невозможно.
Принятые проектные решения позволяют описать символическое
представление кодового формата SP следующим образом:
union TSymCode32
{
// Type SYMbolic CODE 32bits:
// symbolic representation
// of 32bits code
char
Value[ 33 ];
TSymByte
Byte[ 4 ];
);
static TSymCode32
SymFloat;
// as Cstring
// the same as separate
// symbolic bytes
// SYMbolic representation
// of a FLOAT item
Экземпляр SymFloat объединения TSymCode32 размещен в стати
ческой памяти. Завершающий нульсимвол ‘\0’ будет записан в ва
риант SymFloat.Value за счет инициализации по умолчанию.
Символическое представление кодового формата DP опишем ана
логично:
union TSymCode64
{
// Type SYMbolic CODE 64bits
// symbolic representation
// of 64bits code
char
Value[ 65 ];
TSymByte
Byte[ 8 ];
};
static TSymCode64
SymDouble;
// as Cstring
// the same as separate
// symbolic bytes
// SYMbolic representation
// of a DOUBLE item
4.2. Преобразование двоичного кода
в символическое представление
С учетом проектных решений, которые приняты в подразд. 4.1,
преобразование двоичного кода в символическое представление реа
лизовано с использованием двух уровней иерархии.
Нижний уровень – это преобразование одного двоичного байта в
один символический байт – функция BinByteToSymByte:
// convert BINary BYTE TO SYMbolic BYTE
//
void BinByteToSymByte
43
(
unsigned char
TSymByte &
)
Source,
Target
// binary byte
// symbolic byte
unsigned int
Mask;
// to extract a single bit
// of “Source”
// IndeX of SYMbolic BIT in “Target”
{
int
IxSymBit;
Mask = 0x0100;
for ( IxSymBit = 0; IxSymBit < 8; ++IxSymBit )
{
Mask >>= 1;
if ( Source & Mask )
{
Target.Bit[ IxSymBit ] = ‘1’;
}
else
{
Target.Bit[ IxSymBit ] = ‘0’;
}
}
return;
} // end function
Процесс работы функции BinByteToSymByte иллюстрирует
рис. 4.3.
Верхний уровень – это преобразование цепочки двоичных байтов
в цепочку символических байтов – функция BinValueToSymValue.
Source
2 1 1 2 1 2 2 1
1
Mask >>
Target
IxSymBit
2
1
1
2
1
2
2
1
0
1
2
3
4
5
6
7
Рис. 4.3. Преобразование двоичного байта Source в символический
байт Target
44
Здесь нужно не упустить из вида, что старшие двоичные байты зани
мают старшие адреса памяти.
// convert BINary VALUE TO SYMbolic VALUE
//
// source must be a chain of the specified number
//
of binary bytes (higher bytes occupy higher addresses)
//
// target must be long enough to receive
//
the resulting symbolic representation
//
of binary bytes
//
void BinValueToSymValue
(
const unsigned char *
Source,
// chain of binary bytes
int
NumBytes,
// NUMber of BYTES to be
converted
TSymByte *
Target
// chain of symbolic bytes
)
{
int
IxBinByte; // IndeX of BINary BYTE
int
IxSymByte; // IndeX of SYMbolic BYTE
IxBinByte = NumBytes;
for ( IxSymByte = 0; IxSymByte < NumBytes; ++IxSymByte )
{
—IxBinByte;
BinByteToSymByte( Source[ IxBinByte ], Target[ IxSymByte
] );
}
return;
} // end function
Процесс работы функции BinValueToSymValue иллюстрирует
рис. 4.4.
Source
IxBinByte
3
2
1
4
Target
IxSymByte
4
1
2
3
Рис. 4.4. Преобразование цепочки двоичных байтов Source в цепочку
символических байтов Target (на примере формата SP)
45
4.3. Преобразование символического представления
в двоичный код
По аналогии с подразд. 4.2, преобразование символического пред
ставления в двоичный код также реализовано с использованием двух
уровней иерархии.
Нижний уровень – это преобразование одного символического бай
та в один двоичный байт – функция SymByteToBinByte:
// convert SYMbolic
//
void SymByteToBinByte
(
const TSymByte &
unsigned char &
)
{
int
“Source”
unsigned int
«Target»
BYTE TO BINary BYTE
Source,
Target
// symbolic byte
// binary byte
IxSymBit;
// IndeX of SYMbolic BIT in
BitToAdd;
// to add a single 1bit to
//
when necessary
Target
= 0x00;
BitToAdd = 0x0100;
for ( IxSymBit = 0; IxSymBit < 8; ++IxSymBit )
{
BitToAdd >>= 1;
if ( Source.Bit[ IxSymBit ] == ‘1’ )
{
Target |= BitToAdd;
}
}
return;
} // end function
Процесс работы функции SymByteToBinByte иллюстрирует
рис. 4.5.
Верхний уровень – это преобразование цепочки символических
байтов в цепочку двоичных байтов – функция SymValueToBinValue.
Здесь опятьтаки нужно не упустить из вида, что старшие двоичные
байты занимают старшие адреса памяти.
// convert SYMbolic VALUE TO BINary VALUE
//
// source must be a correct chain
//
of the specified number of symbolic bytes
46
//
// target must be long enough to receive
//
the resulting binary code
//
(higher bytes occupy higher addresses)
//
void SymValueToBinValue
(
const TSymByte * Source,
// chain of symbolic bytes
int
NumBytes,
// NUMber of BYTES to be
converted
unsigned char *
Target
// chain of binary bytes
)
{
int
IxSymByte; // IndeX of SYMbolic BYTE
int
IxBinByte; // IndeX of BINary BYTE
IxBinByte = NumBytes;
for ( IxSymByte = 0; IxSymByte < NumBytes; ++IxSymByte )
{
—IxBinByte;
SymByteToBinByte( Source[ IxSymByte ], Target[ IxBinByte
] );
}
return;
} // end function
Source
IxSymBit
0
1
2
3
4
5
6
7
2
1
1
2
1
2
2
1
BitToAdd >>
1
2 1 1 2 1 2 2 1
Target
Рис. 4.5. Преобразование символического байта Source в двоичный
байт Target
47
Source
IxSymByte
4
IxBinByte
1
3
2
2
1
3
4
Target
Рис. 4.6. Преобразование цепочки символических байтов Source в
цепочку двоичных байтов Target (на примере формата SP)
Процесс работы функции SymValueToBinValue иллюстрирует
рис. 4.6.
4.4. Текст программы и результаты ее работы
Полный текст программы, которая обеспечивает возможность изу
чения кодовых форматов с плавающей точкой, приведен в приложе
нии. Текст – самодокументированный, т. е. приспособленный для
чтения без какихлибо дополнительных документов или устных по
яснений.
Хотя программа написана на языке С++, она использует только
незначительные расширения языка Си в языке С++ (например, тип
bool и данные типа ссылка). Это сделано с целью обеспечения воз
можности использования программы уже при изучении языка Си.
Результаты работы этой программы следующие.
CF_Float.out
12.01.2006
Values in single precision code format
Some normalized values
Value: 5.000000e001 Code:
0 01111110 00000000000000000000000
Value: 1.000000e+000 Code:
0 01111111 00000000000000000000000
Value: 1.500000e+000 Code:
0 01111111 10000000000000000000000
48
Value: 2.000000e+000 Code:
0 10000000 00000000000000000000000
Some extreme and special values (in decreasing order)
Positive INFinity
Value: 1.#INF00e+000 Code:
0 11111111 00000000000000000000000
Maximal positive normalized value
Value: 3.402823e+038 Code:
0 11111110 11111111111111111111111
Minimal positive normalized value
Value: 1.175494e038 Code:
0 00000001 00000000000000000000000
Minimal positive denormalized value
Value: 1.401298e045 Code:
0 00000000 00000000000000000000001
Positive null value
Value: 0.000000e+000 Code:
0 00000000 00000000000000000000000
Negative null value
Value: 0.000000e+000 Code:
1 00000000 00000000000000000000000
Negative INFinity
Value: 1.#INF00e+000 Code:
1 11111111 00000000000000000000000
INDefiniteness
Value: 1.#IND00e+000 Code:
1 11111111 10000000000000000000000
Values in double precision code format
Some normalized values
Value: 5.000000e001 Code:
0 01111111110 0000000000000000000000000000000000000000000000000000
Value: 1.000000e+000 Code:
0 01111111111 0000000000000000000000000000000000000000000000000000
Value: 1.500000e+000 Code:
0 01111111111 1000000000000000000000000000000000000000000000000000
Value: 2.000000e+000 Code:
0 10000000000 0000000000000000000000000000000000000000000000000000
Some extreme and special values (in decreasing order)
Maximal positive Quiet NotaNumber
Value: 1.#QNAN0e+000 Code:
0 11111111111 1111111111111111111111111111111111111111111111111111
Minimal positive Quiet NotaNumber
Value: 1.#QNAN0e+000 Code:
49
0 11111111111 1000000000000000000000000000000000000000000000000000
Maximal positive Signaling NotaNumber
Value: 1.#SNAN0e+000 Code:
0 11111111111 0111111111111111111111111111111111111111111111111111
Minimal positive Signaling NotaNumber
Value: 1.#SNAN0e+000 Code:
0 11111111111 0000000000000000000000000000000000000000000000000001
Positive INFinity
Value: 1.#INF00e+000 Code:
0 11111111111 0000000000000000000000000000000000000000000000000000
Maximal positive normalized value
Value: 1.797693e+308 Code:
0 11111111110 1111111111111111111111111111111111111111111111111111
Minimal positive normalized value
Value: 2.225074e308 Code:
0 00000000001 0000000000000000000000000000000000000000000000000000
Minimal positive denormalized value
Value: 4.940656e324 Code:
0 00000000000 0000000000000000000000000000000000000000000000000001
Negative INFinity
Value: 1.#INF00e+000 Code:
1 11111111111 0000000000000000000000000000000000000000000000000000
Maximal negative Signaling NotaNumber
Value: 1.#SNAN0e+000 Code:
1 11111111111 0000000000000000000000000000000000000000000000000001
Minimal negative Signaling NotaNumber
Value: 1.#SNAN0e+000 Code:
1 11111111111 0111111111111111111111111111111111111111111111111111
INDefiniteness
Value: 1.#IND00e+000 Code:
1 11111111111 1000000000000000000000000000000000000000000000000000
Maximal negative Quiet NotaNumber
Value: 1.#QNAN0e+000 Code:
1 11111111111 1000000000000000000000000000000000000000000000000001
Minimal negative Quiet NotaNumber
Value: 1.#QNAN0e+000 Code:
1 11111111111 1111111111111111111111111111111111111111111111111111
Results of operations on some special values
Result of ( +INFinity + (INFinity) )
Value: 1.#IND00e+000 Code:
1 11111111111 1000000000000000000000000000000000000000000000000000
Result of ( 0.0 * (+INFinity) )
Value: 1.#IND00e+000 Code:
1 11111111111 1000000000000000000000000000000000000000000000000000
50
Result of ( 0.0 / 0.0 )
Value: 1.#IND00e+000 Code:
1 11111111111 1000000000000000000000000000000000000000000000000000
Result of ( 1.0 / 0.0 )
Value: 1.#INF00e+000 Code:
0 11111111111 0000000000000000000000000000000000000000000000000000
Result of ( 1.0 / 0.0 )
Value: 1.#INF00e+000 Code:
1 11111111111 0000000000000000000000000000000000000000000000000000
Result of ( +INFinity / +INFinity )
Value: 1.#IND00e+000 Code:
1 11111111111 1000000000000000000000000000000000000000000000000000
Result of ( +INFinity / INFinity )
Value: 1.#IND00e+000 Code:
1 11111111111 1000000000000000000000000000000000000000000000000000
End Of File
4.5. Критерии качества программного обеспечения
Критерии качества, которые использованы в этой книге при раз
работке ПО, основаны на учете объективных психологических фак
торов восприятия текста человеком, а также на многолетнем опыте
лучших специалистов [8].
Наша главная задача при разработке ПО состоит не в том, чтобы
проинструктировать компьютер, а в том, чтобы объяснить другим
людям, какие действия компьютера нам нужны.
К сожалению, формально доказать необходимость разработки ПО
с использованием этих критериев невозможно. Кроме того, создание
понятных текстов требует как интеллектуальных усилий, так и вре
мени.
Эти затраты не напрасны: они многократно окупаются на этапах
отладки и сопровождения ПО. Но это принимают во внимание дале
ко не все.
Так называемые «настоящие программисты» стремятся написать
возможно более короткие тексты программ на том основании, что
«программы пишутся для компьютера, а не для человека». Получа
ется текст, разобраться в котором можно только ценой больших уси
лий.
Такой текст сплошь и рядом оказывается непонятным даже для
его автора – спустя всего 1–2 месяца после написания.
Как следствие, программа содержит много скрытых ошибок (что
приводит к большим затратам на отладку) и практически непригод
51
на для сопровождения (что приводит к финансовому краху фирмы
производителя ПО).
Для иллюстрации того, как НЕ СЛЕДУЕТ разрабатывать про
граммы, приведем примерпародию, который называется
Рассуждения «настоящего программиста»
Рассмотрим функцию BinValueToSymValue с целью ее надлежа
щей переработки.
Начнем с того, что ликвидируем понятие «символический байт» и
тип TSymByte – ведь «символические байты» существуют только в
воображении.
Затем заменим длинные идентификаторы – их долго набирать, и
они раздражают при чтении.
Для имени переменной вполне достаточно одной буквы. Получа
ется следующая «таблица соответствия имен»:
b
n
s
v
m
i
j
:
:
:
:
:
:
:
Binary source;
Number of binary bytes;
Symbolic target;
Value of current binary byte;
Mask;
index of binary byte;
index of binary bit.
Бесспорно, при выборе нетривиальных имен мнемоника учтена, а
обозначения i и j для индексов общеприняты.
Название функции тоже слишком длинное. Заменим его на корот
кое:
b2s : Binary TwO Symbolic.
Цифра 2 в новом названии функции употреблена вместо английско
го слова TWO – и то, и другое произносится поанглийски одинаково.
Внутренняя функция BinByteToSymByte не нужна – она очень про
стая, и ее вызов следует заменить соответствующим циклом. Также
уберем разные излишества, вроде инструкции return в конце.
Комментарии оставим только самые необходимые: те, на которых
настаивает начальство (лично мне комментарии мешают, а компи
лятор их вообще игнорирует).
Получаем:
// Binary TwO Symbolic
void b2s( unsigned char *b, int n, char *s )
{
unsigned v, m;
52
for ( int i=n1; i>=0; i— )
{
v=b[i];
m=0x0100;
for ( int j=7; j>=0; j— )
{
m >>= 1;
if ( v&m )
{
*s=’1’;
}
else
{
*s=’0’;
}
s++;
}
}
*s=’\0’;
}
Многострочная инструкция if устанавливает значение очередного
«символического бита». Требуемые действия лучше записать с помо
щью условной операции, в одной строке:
// Binary TwO Symbolic
void b2s( unsigned char *b, int n, char *s )
{
unsigned v, m;
for ( int i=n1; i>=0; i— )
{
v=b[i];
m=0x0100;
for ( int j=7; j>=0; j— )
{
m >>= 1;
*s = v&m ? ‘1’ : ‘0’;
s++;
}
}
*s=’\0’;
}
Все три действия, которые выполняются в теле внутреннего цик
ла for, можно осуществить в однойединственной инструкциивыра
жении. При этом составная инструкция для тела цикла не понадо
бится:
53
// Binary TwO Symbolic
void b2s( unsigned char *b, int n, char *s )
{
unsigned v, m;
for ( int i=n1; i>=0; i— )
{
v=b[i];
m=0x0100;
for ( int j=7; j>=0; j— )
*s++ = v&(m>>=1) ? ‘1’ : ‘0’;
}
*s=’\0’;
}
Управлять завершением внутреннего цикла for можно по значе
нию маски m, так что индекс j станет ненужным. При этом внутрен
ний цикл for превратится в цикл while:
// Binary TwO Symbolic
void b2s( unsigned char *b, int n, char *s )
{
unsigned v, m;
for ( int i=n1; i>=0; i— )
{
v=b[i];
m=0x0100;
while ( m>>=1 )
*s++ = v&m ? ‘1’ : ‘0’;
}
*s=’\0’;
}
Вид функции улучшился, но возможны дальнейшие сокращения.
Например, индекс i используется в теле внешнего цикла for всего
один раз. Поэтому его можно уменьшать не в заголовке цикла for, а в
связи с этим единственным использованием.
Следует также устранить излишние подробности, вроде сравне
ния индекса i с нулем:
// Binary TwO Symbolic
void b2s( unsigned char *b, int n, char *s )
{
unsigned v, m;
for ( int i=n; i; )
{
v=b[—i];
m=0x0100;
54
while ( m>>=1 ) *s++ = v&m ? ‘1’ : ‘0’;
}
*s=’\0’;
}
Тело цикла for содержит цикл while и подготовку к его выполне
нию (два присваивания). Поэтому здесь правильнее употребить цикл
for.
Кроме того, тело цикла while состоит из всего одной инструкции
выражения, и эту инструкцию можно записать как часть «заголов
ка» цикла for.
Получаем:
// Binary TwO Symbolic
void b2s( unsigned char *b, int n, char *s )
{
for ( int i=n; i; )
for ( unsigned v=b[—i], m=0x0100; m>>=1; *s++=v&m?’1':’0' );
*s=’\0’;
}
Конечно, и этот вариант еще далек от совершенства. Например,
можно убрать необязательные пробелы и ненужные отступы. И во
обще, форматированием следует заниматься только после того, как
программа заработает, и только потому, что этого требует началь
ство.
Таким образом, с самого начала текст должен был быть приблизи
тельно таким:
// Binary TwO Symbolic
void b2s(unsigned char *b,int n,char *s){
for(int i=n;i;)
for(unsigned v=b[—i],m=256;m>>=1;*s++= v&m?’1':’0');
*s=0;
}
Сопоставим результат рационального сокращения текста с исход
ным вариантом – без комментариев (настоящему программисту они
только мешают).
Конец рассуждений «настоящего программиста»
Оценить полученный результат и в самом деле можно поразному.
Комуто понравится лаконичность текста. Другой оценит изобре
тательность его автора.
Но руководитель разработки сложной программы размером не
сколько сот тысяч строк отнесется иначе: ему нужны не замыслова
55
тые изобретения, а простые, понятные, документированные програм
мы. Если требуется работоспособная программа, пригодная для даль
нейшего сопровождения, то на первый план выходят ясность, на
дежность, воспроизводимость.
Что же касается качества машинного кода программы, то подста
новку тела «очень простых» функций вместо их вызова должен осу
ществлять не программист, а компилятор. Например, рассмотрен
ные функции BinByteToSymByte и SymByteToBinByte можно (и, ве
роятно, следует) сделать подставляемыми (inline). Тогда любой со
временный компилятор языка С++ сформирует рабочую программу,
эффективность которой не будет отличаться от результата компиля
ции итога деятельности «настоящего программиста».
К сожалению, точно определить понятие «хорошая программа»
не представляется возможным. Любой язык программирования ос
тается лишь инструментом или, точнее, комплектом инструментов
для интеллектуального труда. Комплект инструментов должен быть
полным и удобным. Это означает, что для каждого вида работ дол
жен найтись подходящий инструмент. Язык С++ как инструмент
программирования вполне удовлетворяет этим требованиям. Осталь
ное зависит от мастера.
56
Приложение
Полный текст программы для изучения кодовых форматов
с плавающей точкой
// FILE
: Main.cpp
//
// PROJECT
: CF_Float
//
(Code Formats for FLOATing point values)
//
// TASK
: explore IBM PC floating point code formats
//
using MS Visual C++ 6.0
//
(Win32 Console Application)
//
// PURPOSE
: main function of the project
//
// SOURCE FILES
: Main.cpp
//
Convers.cpp
//
DblPrec.cpp
//
SnglPrec.cpp
//
// HEADER FILES
: CF_Float.hpp
//
// NOTES
: none
//
// AUTHOR
: Michael F.LEKAREV
// DATE
: 12.01.2006
//
// (C) Copyright
Michael F. LEKAREV 2006. All rights reserved
//
// CHANGES
// RefNo. Date:
Who:
Detail:
//
dd.mm.yyyy
#include <stdio.h>
// FILE, stderr,
// fopen(), fclose(),
// fprintf()
#include <stdlib.h>
// exit()
#include “CF_Float.hpp”
// Global data
//
FILE *
Output;
// will contain
// results of processing
// Local functions of the file
//
static void ShowFloats( void );
57
static void ShowDoubles( void );
static void ShowDoubleResults( void );
// Main.cpp Page 02
// MAIN:
// main function of the project
//
int main( void )
{
// Open “Output”
//
Output = fopen( “CF_Float.out”, “wt” );
if (Output == 0)
{
fprintf( stderr,
“File \”CF_Float.out\” not opened for output\n”
);
exit( 1 );
}
fprintf( Output,
“\n”
“
CF_Float.out
12.01.2006 \n\n”
);
// Show different values of type “float”
//
ShowFloats( );
// Show different values of type “double”
//
ShowDoubles( );
// Show results of operations
// on some special values of type “double”
//
ShowDoubleResults( );
// Finish processing
//
fprintf( Output,
“\n”
“
End Of File \n”
);
fclose( Output );
return ( 0 );
} // end function
// Main.cpp Page 03
// SHOW FLOATS:
// show different values of “float”
// (i.e., values in single precision code format)
58
//
static void ShowFloats( void )
{
char *
SymValue;
// for comfortable construction
// of a SYMbolic VALUE
// Print title of the output’s part
//
fprintf( Output,
“\n\n”
“
Values in single precision code format \n”
);
// Show some normalized values
//
fprintf( Output,
“\n\n”
“
Some normalized values \n\n”
);
SetBinFloatValue( 0.5F );
ShowFloat( );
SetBinFloatValue( 1.0F );
ShowFloat( );
SetBinFloatValue( 1.5F );
ShowFloat( );
SetBinFloatValue( 2.0F );
ShowFloat( );
// Main.cpp Page 04
// Show some extreme and special values (in decreasing order)
//
fprintf( Output,
“\n”
“
Some extreme and special values (in decreasing order) \n\n”
);
// Positive INFinity
//
fprintf( Output,
“
Positive INFinity \n”
);
SymValue = “0”
// sign
“11111111”
// biased exponent
“00000000000000000000000”; // mantissa
SetSymFloatValue( SymValue );
ShowFloat( );
// Maximal positive normalized value
//
fprintf( Output,
“
Maximal positive normalized value \n”
59
);
SymValue = “0”
// sign
“11111110”
// biased exponent
“11111111111111111111111”; // mantissa
SetSymFloatValue( SymValue );
ShowFloat( );
// Minimal positive normalized value
//
fprintf( Output,
“
Minimal positive normalized value \n”
);
SymValue = “0”
// sign
“00000001”
// biased exponent
“00000000000000000000000”; // mantissa
SetSymFloatValue( SymValue );
ShowFloat( );
// Minimal positive denormalized value
//
fprintf( Output,
“
Minimal positive denormalized value \n”
);
SymValue = “0”
// sign
“00000000”
// biased exponent
“00000000000000000000001”; // mantissa
SetSymFloatValue( SymValue );
ShowFloat( );
// Main.cpp
Page 05
// Positive null value
//
fprintf( Output,
“
Positive null value \n”
);
SymValue = “0”
// sign
“00000000”
// biased exponent
“00000000000000000000000”; // mantissa
SetSymFloatValue( SymValue );
ShowFloat( );
// Negative null value
//
fprintf( Output,
“
Negative null value \n”
);
SymValue = “1”
// sign
“00000000”
// biased exponent
“00000000000000000000000”; // mantissa
SetSymFloatValue( SymValue );
60
ShowFloat( );
// Negative INFinity
//
fprintf( Output,
“
Negative INFinity \n”
);
SymValue = “1”
// sign
“11111111”
// biased exponent
“00000000000000000000000”; // mantissa
SetSymFloatValue( SymValue );
ShowFloat( );
// INDefiniteness
//
fprintf( Output,
“
INDefiniteness \n”
);
SymValue = “1”
// sign
“11111111”
// biased exponent
“10000000000000000000000”; // mantissa
SetSymFloatValue( SymValue );
ShowFloat( );
return;
} // end function “ShowFloats”
// Main.cpp Page 06
// SHOW DOUBLES:
// show different values of “double”
// (i.e., values in double precision code format)
//
static void ShowDoubles( void )
{
char *
SymValue; // for comfortable construction
// of a SYMbolic VALUE
// Print title of the output’s part
//
fprintf( Output,
“\n\n”
“
Values in double precision code format \n”
);
// Show some normalized values
//
fprintf( Output,
“\n\n”
“
Some normalized values \n\n”
);
SetBinDoubleValue( 0.5 );
61
ShowDouble( );
SetBinDoubleValue( 1.0 );
ShowDouble( );
SetBinDoubleValue( 1.5 );
ShowDouble( );
SetBinDoubleValue( 2.0 );
ShowDouble( );
// Main.cpp Page 07
// Show some extreme and special values (in decreasing order)
//
fprintf( Output,
“\n”
“
Some extreme and special values (in decreasing order) \n\n”
);
// Maximal positive Quiet NotaNumber
//
fprintf( Output,
“
Maximal positive Quiet NotaNumber \n”
);
SymValue = “0”
// sign
“11111111111”
// biased exponent
“11111111111111111111111111” // mantissa
“11111111111111111111111111”;
SetSymDoubleValue( SymValue );
ShowDouble( );
// Minimal positive Quiet NotaNumber
//
fprintf( Output,
“
Minimal positive Quiet NotaNumber \n”
);
SymValue = “0”
// sign
“11111111111”
// biased exponent
“10000000000000000000000000” // mantissa
“00000000000000000000000000”;
SetSymDoubleValue( SymValue );
ShowDouble( );
// Maximal positive Signaling NotaNumber
//
fprintf( Output,
“
Maximal positive Signaling NotaNumber \n”
);
SymValue = “0”
// sign
“11111111111”
// biased exponent
“01111111111111111111111111” // mantissa
“11111111111111111111111111”;
SetSymDoubleValue( SymValue );
62
ShowDouble( );
// Minimal positive Signaling NotaNumber
//
fprintf( Output,
“
Minimal positive Signaling NotaNumber \n”
);
SymValue = “0”
// sign
“11111111111”
// biased exponent
“00000000000000000000000000” // mantissa
“00000000000000000000000001”;
SetSymDoubleValue( SymValue );
ShowDouble( );
// Main.cpp
Page 08
// Positive INFinity
//
fprintf( Output,
“
Positive INFinity \n”
);
SymValue = “0”
// sign
“11111111111”
// biased exponent
“00000000000000000000000000” // mantissa
“00000000000000000000000000”;
SetSymDoubleValue( SymValue );
ShowDouble( );
// Maximal positive normalized value
//
fprintf( Output,
“
Maximal positive normalized value \n”
);
SymValue = “0”
// sign
“11111111110”
// biased exponent
“11111111111111111111111111” // mantissa
“11111111111111111111111111”;
SetSymDoubleValue( SymValue );
ShowDouble( );
// Minimal positive normalized value
//
fprintf( Output,
“
Minimal positive normalized value \n”
);
SymValue = “0”
// sign
“00000000001”
// biased exponent
“00000000000000000000000000” // mantissa
“00000000000000000000000000”;
SetSymDoubleValue( SymValue );
ShowDouble( );
63
// Minimal positive denormalized value
//
fprintf( Output,
“
Minimal positive denormalized value \n”
);
SymValue = “0”
// sign
“00000000000”
// biased exponent
“00000000000000000000000000” // mantissa
“00000000000000000000000001”;
SetSymDoubleValue( SymValue );
ShowDouble( );
// Negative INFinity
//
fprintf( Output,
“
Negative INFinity \n”
);
SymValue = “1”
// sign
“11111111111”
// biased exponent
“00000000000000000000000000” // mantissa
“00000000000000000000000000”;
SetSymDoubleValue( SymValue );
ShowDouble( );
// Main.cpp
Page 09
// Maximal negative Signaling NotaNumber
//
fprintf( Output,
“
Maximal negative Signaling NotaNumber \n”
);
SymValue = “1”
// sign
“11111111111”
// biased exponent
“00000000000000000000000000” // mantissa
“00000000000000000000000001”;
SetSymDoubleValue( SymValue );
ShowDouble( );
// Minimal negative Signaling NotaNumber
//
fprintf( Output,
“
Minimal negative Signaling NotaNumber \n”
);
SymValue = “1”
// sign
“11111111111”
// biased exponent
“01111111111111111111111111” // mantissa
“11111111111111111111111111”;
SetSymDoubleValue( SymValue );
ShowDouble( );
64
// INDefiniteness
//
fprintf( Output,
“
INDefiniteness \n”
);
SymValue = “1”
// sign
“11111111111”
// biased exponent
“10000000000000000000000000” // mantissa
“00000000000000000000000000”;
SetSymDoubleValue( SymValue );
ShowDouble( );
// Maximal negative Quiet NotaNumber
//
fprintf( Output,
“
Maximal negative Quiet NotaNumber \n”
);
SymValue = “1”
// sign
“11111111111”
// biased exponent
“10000000000000000000000000” // mantissa
“00000000000000000000000001”;
SetSymDoubleValue( SymValue );
ShowDouble( );
// Minimal negative Quiet NotaNumber
//
fprintf( Output,
“
Minimal negative Quiet NotaNumber \n”
);
SymValue = “1”
// sign
“11111111111”
// biased exponent
“11111111111111111111111111” // mantissa
“11111111111111111111111111”;
SetSymDoubleValue( SymValue );
ShowDouble( );
return;
} // end function “ShowDoubles”
// Main.cpp Page 10
// SHOW DOUBLE RESULTS:
// show results of operations
// on some special values of type “double”
// (i.e., values in double precision code format)
//
static void ShowDoubleResults( void )
{
char *
SymValue;
// for comfortable construction
//
of a SYMbolic VALUE
double
PosInfinity;
65
// POSitive INFINITY value
double
NegInfinity;
// NEGative INFINITY value
double
PosNull;
// POSitive NULL value
double
NegNull;
// NEGative NULL value
// Print title of the output’s part
//
fprintf( Output,
“\n\n”
“
Results of operations on some special values
\n\n”
);
/* Set up special values */
// Positive INFinity
//
SymValue = “0”
// sign
“11111111111”
// biased exponent
“00000000000000000000000000” // mantissa
“00000000000000000000000000”;
SetSymDoubleValue( SymValue );
PosInfinity = GetBinDoubleValue( );
// Negative INFinity
//
SymValue = “1”
// sign
“11111111111”
// biased exponent
“00000000000000000000000000” // mantissa
“00000000000000000000000000”;
SetSymDoubleValue( SymValue );
NegInfinity = GetBinDoubleValue( );
// Positive NULL
//
SymValue = “0”
// sign
“00000000000”
// biased exponent
“00000000000000000000000000” // mantissa
“00000000000000000000000000”;
SetSymDoubleValue( SymValue );
PosNull = GetBinDoubleValue( );
// Negative NULL
//
SymValue = “1”
// sign
“00000000000”
// biased exponent
“00000000000000000000000000” // mantissa
“00000000000000000000000000”;
SetSymDoubleValue( SymValue );
NegNull = GetBinDoubleValue( );
// Main.cpp Page 11
66
// Result of ( +INFinity + (INFinity) )
//
fprintf( Output,
“
Result of ( +INFinity + (INFinity)
);
SetBinDoubleValue( PosInfinity + NegInfinity
ShowDouble( );
// Result of ( 0.0 * (+INFinity) )
//
fprintf( Output,
“
Result of ( 0.0 * (+INFinity) ) \n”
);
SetBinDoubleValue( PosNull * PosInfinity );
ShowDouble( );
// Result of ( 0.0 / 0.0 )
//
fprintf( Output,
“
Result of ( 0.0 / 0.0 ) \n”
);
SetBinDoubleValue( PosNull / PosNull );
ShowDouble( );
// Result of ( 1.0 / 0.0 )
//
fprintf( Output,
“
Result of ( 1.0 / 0.0 ) \n”
);
SetBinDoubleValue( 1.0 / PosNull );
ShowDouble( );
// Result of ( 1.0 / 0.0 )
//
fprintf( Output,
“
Result of ( 1.0 / 0.0 ) \n”
);
SetBinDoubleValue( 1.0 / PosNull );
ShowDouble( );
// Result of ( +INFinity / +INFinity )
//
fprintf( Output,
“
Result of ( +INFinity / +INFinity )
);
SetBinDoubleValue( PosInfinity / PosInfinity
ShowDouble( );
// Result of ( +INFinity / INFinity )
//
fprintf( Output,
) \n”
);
\n”
);
67
“
Result of ( +INFinity / INFinity ) \n”
);
SetBinDoubleValue( PosInfinity / NegInfinity );
ShowDouble( );
return;
} // end function “ShowDoubleResults”
// EOF
// FILE
: CF_Float.hpp
//
// PROJECT
: CF_Float
//
// PURPOSE
: definitions of data types
//
and prototypes of all functions of the project
//
// NOTES
: none
//
// AUTHOR
: Michael F.LEKAREV
// DATE
: 12.01.2006
//
// (C) Copyright
Michael F. LEKAREV 2006. All rights reserved
//
// CHANGES
// RefNo.
Date:
Who:
Detail:
//
dd.mm.yyyy
#ifndef _CF_FLOAT_HPP
#define _CF_FLOAT_HPP
/* Data types for symbolic representation
**
of bit strings
*/
struct TSymByte
// Type SYMbolic BYTE
{
//
symbolic representation
//
of a binary byte
char
Bit[ 8 ];
// symbolic bits
};
union TSymCode32
// Type SYMbolic CODE 32bits:
{
//
symbolic representation
//
of 32bits code
char
Value[ 33 ];
// as Cstring
TSymByte
Byte[ 4 ]; // the same as separate
//
symbolic bytes
};
union TSymCode64
// Type SYMbolic CODE 64bits:
{
//
symbolic representation
//
of 64bits code
char
Value[ 65 ];
68
TSymByte
Byte[ 8 ];
// as Cstring
// the same as separate
//
symbolic bytes
};
// CF_Float.cpp
// Functions of file “Convers.cpp
//
bool SymValueIsCorrect
(
const char *
Source,
int
NumSymBits
);
void Substr
(
const char *
Source,
int
IxSubStart,
int
SubLen,
char *
Target
);
void SymValueToBinValue
(
const TSymByte *
Source,
int
NumBytes,
unsigned char *
Target
);
void BinValueToSymValue
(
const unsigned char *
Source,
int
NumBytes,
TSymByte *
Target
);
// Functions of file “DblPrec.cpp
//
void SetSymDoubleValue
(
const char *
Value
);
void SetBinDoubleValue
(
double
Value
);
double GetBinDoubleValue( void );
void ShowDouble( void );
// Functions of file “SnglPrec.cpp
//
Page 02
69
void SetSymFloatValue
(
const char *
Value
);
void SetBinFloatValue
(
float
Value
);
float GetBinFloatValue( void );
void ShowFloat( void );
#endif
// EOF
// FILE
: Convers.cpp
//
// PROJECT
: CF_Float
//
// PURPOSE
: implementation of conversions
//
// NOTES
: none
//
// AUTHOR
: Michael F.LEKAREV
// DATE
: 12.01.2006
//
// (C) Copyright
Michael F. LEKAREV 2006. All rights reserved
//
// CHANGES
// RefNo. Date:
Who:
Detail:
//
dd.mm.yyyy
#include <stdio.h>
// FILE, fprintf()
#include <string.h>
// strlen()
#include “CF_Float.hpp”
// External data
//
extern FILE *
Output;
// will contain
//
results of processing
// Local functions of the file
//
static void SymByteToBinByte
(
const TSymByte &
Source,
unsigned char &
Target
);
static void BinByteToSymByte
(
unsigned char
Source,
70
TSymByte &
);
Target
// Convers.cpp
Page 02
// check whether the source SYMbolic VALUE IS CORRECT
//
// source to be checked must be a correct Cstring
//
// a correct source has the specified number of symbolic bits,
// and each of them is either ‘0’ or ‘1’
//
// RETURNS: true => source is correct
//
false => otherwise
//
bool SymValueIsCorrect
(
const char *
Source,
// to be checked
int
NumSymBits // necessary NUMber of SYMbolic BITS
)
{
int
SourceLen; // actual “Source” LENgth
int
IxSymBit;
// IndeX of SYMbolic BIT in “Source”
SourceLen = strlen( Source );
if ( SourceLen != NumSymBits )
{
fprintf( Output,
“The length of the string: \n”
“
\”%s\” \n”
“
differs from the necessary %d symbolic bits. \n”
“
Conversion cannot be performed. \n”,
Source,
NumSymBits
);
return ( false );
}
for ( IxSymBit = 0; IxSymBit < NumSymBits; ++IxSymBit )
{
switch ( Source[ IxSymBit ] )
{
case ‘0’:
case ‘1’:
break;
default:
fprintf( Output,
“The string: \n”
“
\”%s\” \n”
“
contains character \’%c\’ \n”
71
“
that is not a symbolic bit. \n”
“
Conversion cannot be performed. \n”,
Source,
Source[ IxSymBit ]
);
return ( false );
} // end switch
}
return ( true );
} // end function
// Convers.cpp
Page 03
// extract SUBSTRing of the given source string
// to the target string
//
// source must be a correct Cstring,
// target must be long enough to receive the extracted
substring
//
void Substr
(
const char *
Source,
// the given string
int
IxSubStart, // IndeX of SUBstring’s START
//
in “Source”
int
SubLen,
// SUBstring’s LENgth
char *
Target
// receiver
//
of the extracted substring
)
{
int
SourceLen; // actual “Source” LENgth
int
IxSubChar; // IndeX of processing
//
SUBstring’s CHARacter
char
SubChar;
// processing SUBstring’s CHARacter
if ( IxSubStart < 0 )
{
IxSubStart = 0;
}
SourceLen = strlen( Source );
if ( IxSubStart > SourceLen )
{
IxSubStart = SourceLen;
}
// At this point:
// (0 <= IxSubStart) && (IxSubStart <= SourceLen)
for ( IxSubChar = 0; IxSubChar < SubLen; ++IxSubChar )
{
72
SubChar = Source[ IxSubStart + IxSubChar ];
if ( SubChar == ‘\0’ ) break;
Target[ IxSubChar ] = SubChar;
}
Target[ IxSubChar ] = ‘\0’;
return;
} // end function
// Convers.cpp
Page 04
// convert SYMbolic VALUE TO BINary VALUE
//
// source must be a correct chain
//
of the specified number of symbolic bytes
//
// target must be long enough to receive
//
the resulting binary code
//
(higher bytes occupy higher addresses)
//
void SymValueToBinValue
(
const TSymByte *
Source,
// chain of symbolic bytes
int
NumBytes,
// NUMber of BYTES to be converted
unsigned char *
Target
// chain of binary bytes
)
{
int
IxSymByte; // IndeX of SYMbolic BYTE
int
IxBinByte; // IndeX of BINary BYTE
IxBinByte = NumBytes;
for ( IxSymByte = 0; IxSymByte < NumBytes; ++IxSymByte )
{
—IxBinByte;
SymByteToBinByte( Source[ IxSymByte ], Target[ IxBinByte
] );
}
return;
} // end function
// convert BINary VALUE TO SYMbolic VALUE
//
// source must be a chain of the specified number
//
of binary bytes (higher bytes occupy higher addresses)
//
// target must be long enough to receive
//
the resulting symbolic representation
//
of binary bytes
//
void BinValueToSymValue
73
(
const unsigned char *
Source,
// chain of binary bytes
int
NumBytes,
// NUMber of BYTES to be
converted
TSymByte *
Target
// chain of symbolic bytes
)
{
int
IxBinByte; // IndeX of BINary BYTE
int
IxSymByte; // IndeX of SYMbolic BYTE
IxBinByte = NumBytes;
for ( IxSymByte = 0; IxSymByte < NumBytes; ++IxSymByte )
{
—IxBinByte;
BinByteToSymByte( Source[ IxBinByte ], Target[ IxSymByte
] );
}
return;
} // end function
// Convers.cpp
Page 05
// convert SYMbolic BYTE TO BINary BYTE
//
static void SymByteToBinByte
(
const TSymByte &
Source,
// symbolic byte
unsigned char &
Target
// binary byte
)
{
int
IxSymBit;
// IndeX of SYMbolic BIT in
“Source”
unsigned int
BitToAdd;
// to add a single 1bit to
“Target”
//
when necessary
Target
= 0x00;
BitToAdd = 0x0100;
for ( IxSymBit = 0; IxSymBit < 8; ++IxSymBit )
{
BitToAdd >>= 1;
if ( Source.Bit[ IxSymBit ] == ‘1’ )
{
Target |= BitToAdd;
}
}
return;
} // end function
// convert BINary BYTE TO SYMbolic BYTE
74
//
static void BinByteToSymByte
(
unsigned char
Source,
TSymByte &
Target
)
{
unsigned int
Mask;
// binary byte
// symbolic byte
// to extract a single bit
//
of “Source”
// IndeX of SYMbolic BIT in “Target”
int
IxSymBit;
Mask = 0x0100;
for ( IxSymBit = 0; IxSymBit < 8; ++IxSymBit )
{
Mask >>= 1;
if ( Source & Mask )
{
Target.Bit[ IxSymBit ] = ‘1’;
}
else
{
Target.Bit[ IxSymBit ] = ‘0’;
}
}
return;
} // end function
// EOF
// FILE
: DblPrec.cpp
//
// PROJECT
: CF_Float
//
// PURPOSE
: operations on a Double Precision value
(double)
//
// NOTES
: none
//
// AUTHOR
: Michael F.LEKAREV
// DATE
: 12.01.2006
//
// (C) Copyright
Michael F. LEKAREV 2006. All rights
reserved
//
// CHANGES
// RefNo.
Date:
Who:
Detail:
//
dd.mm.yyyy
#include <stdio.h>
// FILE, fprintf()
#include <string.h>
// strcpy()
75
#include “CF_Float.hpp”
// TSymCode64
// Data type for processing of separate bytes
// of a doubleprecision value
//
union TDoubleCode
{
double
Value;
// double precision value
unsigned char
Byte[ 8 ]; // the same as separate bytes
};
// External data
//
extern FILE *
Output;
// will contain
//
results of processing
// Local data of the file
//
static TDoubleCode
BinDouble; // BINary representation
//
of a DOUBLR item
static TSymCode64
SymDouble; // SYMbolic representation
//
of a DOUBLE item
//
( initialized to ‘\0’
//
by default)
// DblPrec.cpp
Page 02
// SET SYMbolic DOUBLE VALUE
//
void SetSymDoubleValue
(
const char *
Value
// chain of symbolic bits
)
{
if ( !SymValueIsCorrect( Value, 64 ) ) return;
strcpy( SymDouble.Value, Value );
SymValueToBinValue( SymDouble.Byte, 8, BinDouble.Byte );
return;
} // end function
// SET BINary DOUBLE VALUE
//
void SetBinDoubleValue
(
double
Value
// doubleprecision value
)
{
BinDouble.Value = Value;
BinValueToSymValue( BinDouble.Byte, 8, SymDouble.Byte );
return;
} // end function
// GET BINary DOUBLE VALUE
76
//
double GetBinDoubleValue( void )
{
return ( BinDouble.Value );
} // end function
// DblPrec.cpp Page 03
// SHOW DOUBLE value
//
void ShowDouble( void )
{
char
Sign[ 2 ]; // symbolic sign
char
Exp[ 12 ]; // symbolic biased exponent
char
Mantissa[ 53 ];
// symbolic mantissa
Substr( SymDouble.Value, 0, 1, Sign );
Substr( SymDouble.Value, 1, 11, Exp );
Substr( SymDouble.Value, 12, 52, Mantissa );
fprintf( Output,
“Value: %e
Code: \n”
“ %s %s %s \n\n”,
BinDouble.Value,
Sign, Exp, Mantissa
);
return;
} // end function
// EOF
// FILE
: SnglPrec.cpp
//
// PROJECT
: CF_Float
//
// PURPOSE
: operations on a Single Precision value (float)
//
// NOTES
: none
//
// AUTHOR
: Michael F.LEKAREV
// DATE
: 12.01.2006
//
// (C) Copyright
Michael F. LEKAREV 2006. All rights reserved
//
// CHANGES
// RefNo. Date:
Who:
Detail:
//
dd.mm.yyyy
#include <stdio.h>
// FILE, fprintf()
#include <string.h>
// strcpy()
#include “CF_Float.hpp”
// TSymCode32
// Data type for processing of separate bytes
77
// of a singleprecision value
//
union TFloatCode
{
float
Value;
// single precision value
unsigned char
Byte[ 4 ]; // the same as separate bytes
};
// External data
//
extern FILE *
Output;
// will contain
// results of processing
// Local data of the file
//
static TFloatCode
BinFloat;
// BINary representation
// of a FLOAT item
static TSymCode32
SymFloat;
// SYMbolic representation
// of a FLOAT item
// (initialized to ‘\0’
//
by default)
// SnglPrec.cpp Page 02
// SET SYMbolic FLOAT VALUE
//
void SetSymFloatValue
(
const char *
Value
// chain of symbolic bits
)
{
if ( !SymValueIsCorrect( Value, 32 ) ) return;
strcpy( SymFloat.Value, Value );
SymValueToBinValue( SymFloat.Byte, 4, BinFloat.Byte );
return;
} // end function
// SET BINary FLOAT VALUE
//
void SetBinFloatValue
(
float
Value
// singleprecision value
)
{
BinFloat.Value = Value;
BinValueToSymValue( BinFloat.Byte, 4, SymFloat.Byte );
return;
} // end function
// GET BINary FLOAT VALUE
//
float GetBinFloatValue( void )
78
{
return ( BinFloat.Value );
} // end function
// SnglPrec.cpp Page 03
// SHOW FLOAT value
//
void ShowFloat( void )
{
char
Sign[ 2 ]; // symbolic sign
char
Exp[ 9 ];
// symbolic biased exponent
char
Mantissa[ 24 ];
// symbolic mantissa
Substr( SymFloat.Value, 0, 1, Sign );
Substr( SymFloat.Value, 1, 8, Exp );
Substr( SymFloat.Value, 9, 23, Mantissa );
fprintf( Output,
“Value: %e
Code: \n”
“ %s %s %s \n\n”,
BinFloat.Value,
Sign, Exp, Mantissa
);
return;
} // end function
// EOF
Библиографический список
1. Арчер Т. Основы С#. Новейшие технологии: Пер. с англ. М.: Изда
тельскоторговый дом «Русская редакция», 2001. 448 с.
2. Бочков С. О., Субботин Д. М. Язык программирования Си для персо
нального компьютера. М.: Радио и связь, 1990. 384 с.
3. Григорьев В. Л. Микропроцессор i486. Архитектура и программиро
вание: В 2 т. Т. 2: Кн. 2. Аппаратная архитектура. Кн. 3. Устройство с
плавающей точкой. Кн. 4. Справочник по системе команд. М.: ГРАНАЛ,
1993. 382 с.
4. Лекарев М. Ф. Использование формализма Lсети в программах об
работки текстов / СПбГТУ. СПб., 2000. 76 с.
5. Goldberg D. What every computer scientist should know about floating
point arithmetic // ACM Computer Surveys. March 1991.Vol. 23. N 1. P. 5–47.
6. Gosling J., Joy B., Steel G., Bracha G. The Java TM Language
Specification. 3rd Edition. AddisonWesley, 2005. 649 p.
7. IEEE Standard 7541985 for Binary FloatingPoint Arithmetic. New
York: IEEE, 1991. 20 p.
8. Straker D. Cstyle: standards and guidelines. Prentice Hall, International
(UK) Ltd., 1992. 231 p.
9. Stroustrup B. The C++ Programming Language. 2nd Edition. Addison
Wesley, 1991. 669 p.
79
СОДЕРЖАНИЕ
Предисловие ............................................................................
3
1. Общие замечания ..................................................................
1.1. Кодирование и содержание ..................................................
1.2. Кодирование целых значений ...............................................
1.3. Кодирование вещественных значений ...................................
1.4. Нормализация ...................................................................
1.5. Область применения данных с плавающей точкой ...................
4
4
5
6
8
9
2. Кодовые форматы с плавающей точкой ..................................
2.1. Структура кодового формата с плавающей точкой ...................
2.2. Кодовый формат одинарной точности (SP) ..............................
2.3. Кодовый формат двойной точности (DP) .................................
2.4. Кодовый формат расширенной точности (EP) .........................
2.5. Специальные значения ........................................................
2.6. Сводная таблица кодирования данных в форматах SP и DP ......
2.7. Сводная таблица кодирования данных в формате EP ...............
2.8. Неподдерживаемые коды в формате EP .................................
11
11
14
16
18
19
26
28
29
3. Данные с плавающей точкой в языках программирования
высокого уровня ......................................................................
3.1. Переменные с плавающей точкой ..........................................
3.2. Литералы с плавающей точкой .............................................
32
32
33
4. Программа для изучения кодовых форматов с плавающей
точкой .....................................................................................
4.1. Двоичные и символические байты ........................................
4.2. Преобразование двоичного кода в символическое
представление ...................................................................
4.3. Преобразование символического представления
в двоичный код ..................................................................
4.4. Текст программы и результаты ее работы ..............................
4.5. Критерии качества программного обеспечения ......................
46
48
51
Приложение. Полный текст программы для изучения кодовых
форматов с плавающей точкой ....................................................
Библиографический список .......................................................
57
79
80
40
40
43
Документ
Категория
Без категории
Просмотров
2
Размер файла
419 Кб
Теги
lekarev1
1/--страниц
Пожаловаться на содержимое документа