Листинг 3.16 показывает, как можно найти это число (пример Epsilon на компакт-диске).
Листинг 3.16. Поиск машинного эпсилон
procedure TForm1.Button1Click(Sender: TObject);
var
R: Extended;
I: Integer;
begin
R := 1;
while 1 + R/2 > 1 do R := R / 2;
Label1.Caption := FloatToStr(R);
end;
Запустив этот код, мы получим на экране 1.0842021724855Е-19в полном соответствии с нашими теоретическими выкладками.
Примечание
В тех системах, где наблюдается описанная проблема с уменьшением точности, программа выдаст 2.22044604925031Е-16. Если вы увидели у себя это число, добавьте код, который переведет FPU в режим максимальной точности.
А теперь изменим тип переменной R
с Extended
на Double
. Результат не изменится. На Single
— опять не изменится. Но такое поведение лишь на первый взгляд может показаться странным. Давайте подробнее рассмотрим выражение 1 + R / 2 > 1
. Итак, все вычисления (в том числе и сравнение) сопроцессор выполняет с данными типа Extended
. Последовательность действий такова: число R
загружается в регистр сопроцессора, преобразуясь при этом к типу Extended
. Дальше оно делится на 2, а затем к результату прибавляется 1, и все это в Extended
, никакого обратного преобразования в Single или Double
не происходит. Затем это число сравнивается с единицей. Очевидно, что результат сравнения не должен зависеть от исходного типа R
, т.к. диапазона даже типа Single
вполне хватает, чтобы разместить машинное эпсилон.
3.2.13. Методы решения проблем
Подведем итоги сказанному. Значения, которые мы получаем, могут отличаться от ожидаемых, даже если речь идет о простом присваивании. Во многих случаях (например, в научных расчетах) это несущественно, т.к. сам метод расчета дает еще большую погрешность. Проблемы начинаются там, где мы хотим вывести число на экран или сравнить его с другим. Универсальных рецептов на все случаи жизни не существует, но во многих ситуациях помогают следующие советы:
□ Если ваша задача — просто получить "красивое" представление числа на экране, то функцию FloatToStr
заменяйте на ее более мощный аналог FloatToStrF
или на функцию Format
— они позволяют указать желаемое количество символов после точки.
□ Сравнение вещественных чисел следует выполнять с учетом погрешности, т.е. вместо if а = b
… писать if Abs(а - b) < Ерs
…, где Eps
— некоторая величина, задающая допустимую погрешность (в модуле Math, начиная с Delphi 6, существует функция SameValue
, с помощью которой это же условие можно записать как if SameValue(a, b, Eps)
…).
□ Для денежных расчетов следует выбирать тип Currency
, реализующий число с фиксированной, а не плавающей, десятичной точкой. Отметим также, что не следует пытаться решить проблему неточного представления числа (0,100000001490116 вместо 0,1) с помощью функции RoundTo
, поскольку эта функция не может обеспечить точность бо́льшую, чем точность аппаратного представления вещественных чисел.
3.3. Тонкости работы со строками
В этом разделе мы рассмотрим некоторые тонкости работы со строками, которые позволяют лучше понять, какой код генерирует компилятор при некоторых, казалось бы, элементарных действиях. Не все приведенные здесь примеры работают не так, как можно было бы ожидать, так что этот материал немного выходит за рамки главы. Но "подводные камни" здесь мы тоже встретим.
3.3.1. Виды строк в Delphi
Для работы с кодировкой ANSI в Delphi существует три вида строк: AnsiString
, ShortString
и PChar
. Различие между ними заключается в способе хранения строки, а также выделения и освобождения памяти для нее. Зарезервированное слово string
по умолчанию означает тип AnsiString
, но если после нее следует число в квадратных скобках, то это означает тип ShortString
, а число — ограничение по длине. Кроме того, существует опция компилятора Huge strings(управляется также директивами компилятора {$H+/-}
и {$LONGSTRINGS ON/OFF}
, которая по умолчанию включена, но если ее выключить, то слово string
станет эквивалентно ShortString
; или, что то же самое, string[255]
. Эта опция введена для обратной совместимости с Turbo Pascal, в новых программах отключать ее нет нужды. Внутреннее устройство этих типов данных иллюстрирует рис. 3.2.
Рис. 3.2. Устройство различных строковых типов Delphi
Читать дальше
Конец ознакомительного отрывка
Купить книгу