Типы данных:Real – стандартный тип Паскаля. Когда создавался Паскаль, процессоры ещё не имели встроенной поддержки работы с вещественными числами.
Extended,
Double,
Single – введены
Borland в
Turbo Pascal для поддержки сопроцессорных типов. При чём,
Extended – это родной для сопроцессора тип, а
Double,
Single получаются из него простым усечением и не совпадают с
Real, операции с ними значительно быстрее последнего.
Округление:Стандарт
IEEE-754 описывает несколько способов округления вещественных чисел. Так случилось, что разработка стандарта и разработка сопроцессора компанией
Intel совпали.
Intel открыл некоторые спецификации своей разработки, которые и легли в основу стандарта. Один из способов округления носит название «
банковское округление» или «
бухгалтерское округление», который предписывает округлять в сторону ближайшего целого чётного числа. Это округление используется компанией
Intel по умолчанию.
Простыми словами банковское округление выглядит: если цифра перед пятёркой чётная, то округление вниз, иначе вверх.
Сопроцессор:Для управления сопроцессором служат управляемые регистры и команды. В Delphi и FPC эти команды завёрнуты в вызовы функций:
Set8087CW,
Get8087CW,
Default8087CW. Принимая соответствующие параметры команд, позволяют управлять точностью, округлением и др..
К примеру
Set8087CW(Get8087CW and $FCFF or $0200) - переведёт сопроцессор в режим 53-разрядной точности представления мантиссы(дробной части), а
Set8087CW(Get8087CW and $FCFF) - в режим 24-разрядной точности.
У прикладного программиста редко появляется нужда в низкоуровневых вызовах. В модуле
Math, есть функции, с помощью которых легко управлять поведением сопроцессора. Одной из таких, является функция
SetRoundMode(её пара
GetRoundMode), которая управляет округлением, принимая и возвращая один из параметров:
- rmNearest — округление к ближайшему чётному (по умолчанию)
- rmDown — округление вниз
- rmUp — округление вверх
- rmTruncate — отбрасывание дробной части
- Код: Выделить всё
var
rm: TFPURoundingMode;
Begin
rm := SetRoundMode(rmUp); // режим округления вверх , функция возвращает предыдущее значение.
// a := Round(b)
// или другие Ваши действия
SetRoundMode(rm) // обязательно! восстановим сохранённое значение
end;
Особенность WinCE:За работу с вещественными числами в
WinCE отвечает библиотека
fpcrt.dll. Её функции импортируются
coredll.dll. И вся забава в том, что по умолчанию
fpcrt.dll лишь эмулирует работу с сопроцессором, выполняя все операции в целочисленном блоке. Отсюда потеря скорости и расхождение результатов. Вы можете переписать и заменить библиотеку
fpcrt.dll своей реализацией. О том, как это сделать подробно рассказывает
ОРАКУЛ. Это для
WinCE 6.
А вот
Windows Embedded Compact 7 ведёт себя уже по-другому:
fpcrt.dll динамически определяет операции с вещественными числами и решает, как ей использовать аппаратные средства. Об этом также спрашивать у
ОРАКУЛА.
В сети можно найти исходники и откомпилированные модули, также исходники выкладывались на сайте
http://arm.com/.
Danger!Раз уж коснулись сопроцессора, замечу:
На конференциях
Borland активно обсуждались и обсуждаются вопросы, связанные с операциями вещественных чисел. В одном из таких вопросов говорилось, что по неизвестным причинам при работе с вещественными числами программы падали или давали непредсказуемый результат. Оказалось, что при вызове некоторых
API функций менялось управляющее слово в сопроцессоре, а после работы прежнее значение не восстанавливалось. Ну забыл кто-то восстановить.. бывает))). Вскоре виновник был найден и уже начиная с версии
Delphi 7, а также во всех последующих версиях включая текущую(
Delphi XE4), в модуле
Windows.pas встречается такой код:
- Код: Выделить всё
function CreateWindowEx(dwExStyle: DWORD; lpClassName: PChar;
lpWindowName: PChar; dwStyle: DWORD; X, Y, nWidth, nHeight: Integer;
hWndParent: HWND; hMenu: HMENU; hInstance: HINST; lpParam: Pointer): HWND;
var
FPUCW: Word;
begin
FPUCW := Get8087CW;
Result := _CreateWindowEx(dwExStyle, lpClassName, lpWindowName, dwStyle,
X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
Set8087CW(FPUCW);
end;
Ошибка замечалась на более ранних версиях
Windows (
Win98,
Win95), но также были слухи о её появлении и в версиях
Win NT после доустановки каких-либо программ. Если Вы посмотрите на аналогичный модуль из библиотеки
fpc(в данный момент речь идет о версии 2.6.2) Вы ничего подобного там не найдёте. А вот заглянув в модуль
sysutils.pp, можно увидеть следующее:
- Код: Выделить всё
Procedure InitInternational;
var
{ A call to GetSystemMetrics changes the value of the 8087 Control Word on
Pentium4 with WinXP SP2 }
old8087CW: word;
begin
/// workaround for Windows 7 bug, see bug report #18574
old8087CW:=Get8087CW;
SysLocale.MBCS:=GetSystemMetrics(SM_DBCSENABLED)<>0;
{ ** SKIPPED ** }
Set8087CW(old8087CW);
end;
Видно, что кто-то из разработчиков
fpc всё же столкнулся с ошибкой и обезопасил
только себя.
- Почему, зная о проблеме, не завернут сам вызов GetSystemMetrics подобно тому, как это сделали ребята из Borland?!
- Это уверенность в том, что никто не использует вызов GetSystemMetrics?
- ... ?
Для более подробного изучения работы с вещественными числами настоятельно рекомендую обратиться к статьям:
Неочевидные особенности вещественных чисел © Антон Григорьев,
Загадки округления © Алексей Михайличенко.
Обязательно загляните в разделы «обсуждение» статей, где можно найти ответы на возникающие вопросы.