3.2.10. Неожиданная потеря точности

We use cookies. Read the Privacy and Cookie Policy

3.2.10. Неожиданная потеря точности

Изменим в предыдущем примере тип переменной R с Single на Double. Значение, выводимое программой, станет 1.44327637948555E-16. Вполне логичный и предсказуемый результат, т. к. тип Double точнее, чем Single, и, следовательно, все вычисления имеют меньшую погрешность, мы просто обязаны получить более точный результат. Хотя, разумеется, абсолютная точность (т. е. ноль) для нас остается недостижимым идеалом.

А теперь — вопрос на засыпку. Изменится ли результат, если мы заменим Double на более точный Extended? Ответ не такой однозначный, каким его хотелось бы видеть. В принципе, после такой замены вы должны получить -6.7762635780344E-20. Но в некоторых случаях от замены Double на Extended результат не изменится, и вы снова получите 1.44327637948555Е-16. Это зависит от операционной системы и версии Delphi.

Все дело в использовании "неполноценного" Extended. При запуске программы любая система устанавливает такое управляющее слово FPU, чтобы Extended был полноценным. Но затем программа вызывает много разных функций Windows API. Какая-то (или какие-то) из этих многочисленных функций некорректно работают с управляющим словом, меняя его значение и не восстанавливая при выходе. Такая проблема встречается, в основном, в Windows 95 и старых версиях Windows 98. Также имеются сведения о том, что управляющее слово может "портиться" и в Windows NT, причем эффект наблюдался не сразу после установки системы, а лишь через некоторое время, после доустановки других программ. Проблема именно в некорректности поведения системных функций; значение управляющего слова, устанавливаемое системой при запуске программы, всегда одинаково. Таким образом, приходим к неутешительному выводу: к тем проблемам с вещественными числами, которые обусловлены особенностями их аппаратной реализации, добавляются еще и ошибки операционной системы. Правда, радует то, что в последнее время эти ошибки встречаются крайне редко — видимо, новые версии системы от них избавлены. Тем не менее полностью исключать такую возможность нельзя, особенно если ваша программа будет запускаться на старой технике с устаревшими системами. Чтобы приведенный пример всегда выдавал правильное значение -6.7762635780344E-20, достаточно поставить в начале нашей процедуры Set808 °CW(Get8087CW or $0100), и программа в любой системе будет устанавливать сопроцессор в режим максимальной точности.

Примечание

В версиях Delphi по 5-ю включительно, где отсутствует функция Get8087CW, можно использовать такую конструкцию: Set8087CW(Default8087CW). При этом следует учитывать, что она возвращает к начальному состоянию все флаги, а не только интересующий нас. Если это неприемлемо, управляющее слово придется изменять с помощью встроенного ассемблера.

Раз уж мы заговорили об управляющем слове, давайте немного поэкспериментируем с ним. Изменим первую строчку на Set8087CW(Get8087CW and $FCFF or $0200). Тем самым мы перевезем сопроцессор в режим 53-разрядной точности представления мантиссы. Теперь в любой системе мы увидим 1.44327637948555Е-16, несмотря на использование Extended. Если же мы изменим первую строчку на Set8087CW(Get8087CW and $FCFF), то будем работать в режиме 24-разрядной точности. Соответственно, в любой системе будет результат -7.3015691270939Е-8.

Заметим, что при загрузке в 10-байтный регистр сопроцессора числа типа Extended в режиме пониженной точности "лишние" биты не обнуляются. Только результаты математических операций представляются с пониженной точностью. Кроме того, при сравнении двух чисел также учитываются все биты, независимо от точности. Поэтому код, приведенный в листинге 3.10 при выборе любой точности даст Не равно.

Данный текст является ознакомительным фрагментом.