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

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

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

<p>3.2.11. Борьба с потерей точности в VCL</p></span><span>

В том, что описанная проблема с потерей точности встречается все реже, есть заслуга и разработчиков VCL. Зная, вызовы каких функций могут привести к изменению управляющего слова FPU, они перед этими вызовами запоминают управляющее слово, а затем восстанавливают. В более поздних версиях Delphi количество таких "оберток" больше, чем в ранних, поэтому чем новее версия Delphi, тем меньше шанс столкнуться с описанной проблемой. Здесь мы рассмотрим несколько примеров из исходного кода стандартных модулей Delphi 2007.

Для динамической загрузки DLL предназначена API-функция LoadLibrary. В модуле SysUtils для этой функции предлагается обертка, называющаяся SafeLoadLibrary (листинг 3.13).

Листинг 3.13. Функция SysUtils.SafeLoadLibrary

{ SafeLoadLibrary calls LoadLibrary, disabling normal Win32 error message popup dialogs if the requested file can't be loaded. SafeLoadLibrary also preserves the current FPU control word (precision, exception masks) across the LoadLibrary call (in case the DLL you're loading hammers the FPU control word in its initialization, as many MS DLLs do) }

function SafeLoadLibrary(const Filename: string; ErrorMode: UINT): HMODULE;

var

 OldMode: UINT;

 FPUControlWord: Word;

begin

 OldMode := SetErrorMode(ErrorMode); 

 try

  asm

   FNSTCW FPUControlWord

  end;

  try

   Result := LoadLibrary(PChar(Filename));

  finally

   asm

    FNCLEX

    FLDCW FPUControlWord

   end;

  end;

 finally

  SetErrorMode(OldMode);

 end;

end;

Как видно из комментария, проблема в том, что многие системные библиотеки изменяют управляющее слово FPU при своей инициализации.

В функции CreateADOObject (внутренняя функция модуля ADODB) тоже сохраняется и восстанавливается управляющее слово (листинг 3.14).

Листинг 3.14. Функция CreateADOObject модуля ADODB

function CreateADOObject(const ClassID: TGUID): IUnknown;

var

 Status: HResult;

 FPUControlWord: Word;

begin

 asm

  FNSTCW FPUControlWord

 end;

 Status :=

  CoCreateInstance(ClassID, nil, CLSTX_INPROC_SERVER or CLSCTX_LOCAL_SERVER, IUnknown, Result);

 asm

  FNCLEX

  FLDCW FPUControlWord

 end;

 if (Status = REGDB_E_CLASSNOTREG) then

  raise Exception.CreateRes(@SADOCreateError)

 else OleCheck(Status);

end;

Здесь восстанавливать управляющее слово приходится после вызова системной функции CoCreateInstance, создающей СОМ-объект. Но, судя по тому, что больше нигде при вызове CoCreateInstance такой код не используется, проблема не в самой функции, а в тех конкретных ADO-объектах, которые создаются здесь с ее помощью.

Аналогичную защиту можно обнаружить в модуле Dialogs, в методе TCommonDialog.TaskModalDialog. Комментарий к этой защите гласит: "Avoid FPU control word change in NETRAP.dll, NETAPI32.dll, etc".

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже