Читаем О чём не пишут в книгах по Delphi полностью

Что мы увидим, когда нажмем кнопку? Разумеется, не 0.1, иначе не было бы смысла писать этот пример. Мы увидим 0.100000001490116, т.е. расхождение в девятой значащей цифре. Из справки по Delphi мы знаем, что точность типа Single — 7–8 десятичных разряда, так что нас, по крайнем мере, никто не обманывает. В чем же причина? Просто число 0,1 не представимо в виде конечной двоичной дроби, оно равно 0,0(0011). И эта бесконечная двоичная дробь обрубается на 24-х знаках; мы получаем не 0,1, а некоторое приближенное число (какое именно — см. выше). А если мы присвоим переменной R не 0.1, а 0.5? Тогда мы получим на экране 0.5, потому что 0.5 предоставляется в виде конечной двоичной дроби. Немного поэкспериментировав с различными числами, мы заметим, что точно представляются те числа, которые выражаются в виде m/2n, где m, n — некоторые целые числа (разумеется, n не должно превышать 24, а то нам не хватит точности типа Single). В качестве упражнения предлагаем доказать, что любое целое число, для записи которого хватает 24-х двоичных разряда, может быть точно передано типом Single.

Примечание 

Если в этом примере изменить тип переменной R с Single на Double или на Extended, на экран будет выведено 0.1. Но это не значит, что в переменную будет записано ровно 0.1 — это просто особенности работы функции FloatToStr, которая не учитывает столь малую разницу между 0,1 и переданным ей числом.

<p>3.2.7. Сравнение</p>

Теперь попробуем сравнить значение переменной и константы, которую мы ей присвоили (листинг 3.10, пример Compare1 на компакт-диске).

Листинг 3.10. Пример ошибки при сравнении вещественной переменной и константы

procedure TForm1.Button1Click(Sender: TObject);

var

 R: Single;

begin

R := 0.1;

 if R = 0.1 then Label1.Caption := 'Равно'

 else Label1.Caption := 'He равно';

end;

При нажатии кнопки мы увидим надпись Не равно. На первый взгляд это кажется абсурдом. Действительно, мы уже знаем, что переменная R получает значение 0.100000001490116 вместо 0.1. Но ведь "0.1" в правой части равенства тоже должно преобразоваться по тем же законам, т.к. работает аналогичный алгоритм. Тут самое время вспомнить, что FPU работает только с 10-байтным типом Extended, поэтому и левая, и правая часть равенства сначала преобразуется в этот тип, и лишь потом производится сравнение. То число, которое оказалось в переменной R вместо 0.1, хотя и выглядит страшно, но зато представляется в виде конечной двоичной дроби. Информация же о том, что это на самом деле должно означать "0.1", нигде не сохранилась. При преобразовании этого числа в Extended младшие, избыточные по сравнению с типом Single разряды мантиссы просто заполняются нулями, и мы снова получим то же самое число, только записанное в формате Extended. А "0.1" из правой части равенства преобразуется в Extended без промежуточного превращения в Single. Поэтому некоторые из младших разрядов мантиссы будут содержать единицы. Другими словами, мы получим хоть и не точное представление числа 0.1, но все же более близкое к истине, чем 0.100000001490116. 

Из-за таких хитрых преобразований оказывается, что мы сравниваем два близких, но все же не равных числа. Отсюда — закономерный результат в виде надписи Не равно.

Тут уместна аналогия с десятичными дробями. Допустим, в одном случае мы делим 1 на три с точностью до трех знаков и получаем 0,333. Потом мы делим 1 на три с точностью до четырех знаков и получаем 0,3333. Теперь мы хотим сравнить эти два числа. Для этого приводим их к точности в четыре разряда. Получается, что мы сравниваем 0,3330 и 0,3333. Очевидно, что это разные числа.

Если попробовать заменить число 0,1 на 0,5, то мы увидим надпись Равно. Полагаем, что читатели уже догадались, почему, но все же приведем объяснение. Число 0,5 — это конечная двоичная дробь. При прямом приведении ее к типу Extended в младших разрядах оказываются нули. Точно такие же нули оказываются в этих разрядах при превращении числа 0,5 типа Single в тип Extended. Поэтому в результате мы сравниваем два равных числа. Это похоже на процесс деления 1 на 4 с точностью до трех и до четырех значащих цифр. В первом случае получили бы 0,250, во втором — 0,2500. Приведя оба значения к точности в четыре знака, получим сравнение 0,2500 и 0,2500. Очевидно, что эти числа равны.

<p>3.2.8. Сравнение разных типов</p>

Теперь попытаемся сравнить переменную не с константой, а с другой переменной (листинг 3.11, пример Compare2 на компакт-диске).

Листинг 3.11. Пример ошибки при сравнении переменных разных типов

procedure TForm1.Button1Click(Sender: TObject);

var

 R1: Single;

 R2: Double;

begin

 R1 := 0.1;

 R2 := 0.1;

Перейти на страницу:

Похожие книги

Основы программирования в Linux
Основы программирования в Linux

В четвертом издании популярного руководства даны основы программирования в операционной системе Linux. Рассмотрены: использование библиотек C/C++ и стан­дартных средств разработки, организация системных вызовов, файловый ввод/вывод, взаимодействие процессов, программирование средствами командной оболочки, создание графических пользовательских интерфейсов с помощью инструментальных средств GTK+ или Qt, применение сокетов и др. Описана компиляция программ, их компоновка c библиотеками и работа с терминальным вводом/выводом. Даны приемы написания приложений в средах GNOME® и KDE®, хранения данных с использованием СУБД MySQL® и отладки программ. Книга хорошо структурирована, что делает обучение легким и быстрым. Для начинающих Linux-программистов

Нейл Мэтью , Ричард Стоунс , Татьяна Коротяева

ОС и Сети / Программирование / Книги по IT
97 этюдов для архитекторов программных систем
97 этюдов для архитекторов программных систем

Успешная карьера архитектора программного обеспечения требует хорошего владения как технической, так и деловой сторонами вопросов, связанных с проектированием архитектуры. В этой необычной книге ведущие архитекторы ПО со всего света обсуждают важные принципы разработки, выходящие далеко за пределы чисто технических вопросов.?Архитектор ПО выполняет роль посредника между командой разработчиков и бизнес-руководством компании, поэтому чтобы добиться успеха в этой профессии, необходимо не только овладеть различными технологиями, но и обеспечить работу над проектом в соответствии с бизнес-целями. В книге более 50 архитекторов рассказывают о том, что считают самым важным в своей работе, дают советы, как организовать общение с другими участниками проекта, как снизить сложность архитектуры, как оказывать поддержку разработчикам. Они щедро делятся множеством полезных идей и приемов, которые вынесли из своего многолетнего опыта. Авторы надеются, что книга станет источником вдохновения и руководством к действию для многих профессиональных программистов.

Билл де Ора , Майкл Хайгард , Нил Форд

Программирование, программы, базы данных / Базы данных / Программирование / Книги по IT
Программист-прагматик. Путь от подмастерья к мастеру
Программист-прагматик. Путь от подмастерья к мастеру

Находясь на переднем крае программирования, книга "Программист-прагматик. Путь от подмастерья к мастеру" абстрагируется от всевозрастающей специализации и технических тонкостей разработки программ на современном уровне, чтобы исследовать суть процесса – требования к работоспособной и поддерживаемой программе, приводящей пользователей в восторг. Книга охватывает различные темы – от личной ответственности и карьерного роста до архитектурных методик, придающих программам гибкость и простоту в адаптации и повторном использовании.Прочитав эту книгу, вы научитесь:Бороться с недостатками программного обеспечения;Избегать ловушек, связанных с дублированием знания;Создавать гибкие, динамичные и адаптируемые программы;Избегать программирования в расчете на совпадение;Защищать вашу программу при помощи контрактов, утверждений и исключений;Собирать реальные требования;Осуществлять безжалостное и эффективное тестирование;Приводить в восторг ваших пользователей;Формировать команды из программистов-прагматиков и с помощью автоматизации делать ваши разработки более точными.

А. Алексашин , Дэвид Томас , Эндрю Хант

Программирование / Книги по IT