Exp — Функция Delphi


Содержание

Delphi-Help

Exp

Описание

function Exp ( Number : Extended ) : Extended;

Функция Exp является математической функцией, она возвращает экспоненту числа.
Функция Exp берет целое число или число с плавающей точкой и возводит в степень e (2.72).
Использование возможно только для математической обработки.
Exp имеет противоположную ей функцию Ln — натуральный логарифм.

Пример кода

var
float : Double;
begin
// Получение натурального логарифма 2
float := Ln(2);
// Показ этого значения
ShowMessage(‘Ln(2) = ‘+FloatToStr(float));
// Получение экспоненты этого значения — полностью отменяет операцию Ln
float := Exp (float);
// Показ этого значения
ShowMessage(‘Exp(Ln(2)) = ‘+FloatToStr(float));
end;

Ln(2) = 0.693147180559945
Exp(Ln(2)) = 2

Функция Exp

Функция Exp в Паскале (и многих других языках программирования) вычисляет экспоненту. Синтаксис:

function Exp(X : ValReal) : ValReal;

О типе ValReal я рассказывал здесь.

Функция Exp X вычисляет и возвращает экспоненту числа X.

Вычисление экспоненты — это вычисление числа е в степени X. То есть

Подробности см. в видео и читайте в статье далее.

Обратная функция Ln

Если вы помните функцию Ln, то вы также помните, что она вычисляет натуральный логарифм.

Так вот, обратной функцией Exp является функция Ln. Иными словами, обратная функция экспоненциальной функции (экспоненты) — это натуральный логарифм. То есть:

e X = Exp(X) = Exp(Ln(Y)) = Y

Есть ещё вот такая полезная формула:

x Y = e Y ln(x) = Exp(Y * Ln(X))

Из этого следует, что используя функции Ln и Exp, мы можем возвести любое число в любую степень. Сделать это можно, например, так:

Если описать это математическим языком, то приведённое выше выражение будет эквивалентно следующей записи:

Правда, надо сказать, что здесь есть нюансы. Есть частные случаи, когда приведённое выше выражение выдаст неправильный результат. Например, когда Y или X отрицательные числа, или когда они равны нулю. Такие ситуации надо обрабатывать дополнительно. Однако эта статья не о возведении в степень, поэтому мы будем рассматривать эти частные случаи в другой статье.

Пример исходного кода, где используется функция Exp:

Exp — Функция Delphi

Профиль
Группа: Участник
Сообщений: 159
Регистрация: 5.5.2005
Где: Россия

Репутация: нет
Всего: нет

Код
const ExpValue=2.7182818284590452353602874713527;

function ExpEx(Exponent: Double): Double;
var
Cel: Integer;
ExpX: Double;
begin
Result:=1;
if (Exponent>1) or (Exponent =-1) and (Exponent 0) and (Exponent

Может кто-то знает, как еще быстрее организовать эту функцию, например превратить код Delphi в Asm.
А, да меня еще интересует функция Ln(), также как Exp(), как ее можно быстрее организовать, опять же мне большая точность не нужна.
Заранее большое всем спасибо за предоставленную информацию и ответы.

Профиль
Группа: Участник
Сообщений: 3388
Регистрация: 12.3.2006
Где: Тосно

Репутация: 28
Всего: 89

Sunvas
Дата 29.4.2006, 07:10 (ссылка) | (нет голосов) Загрузка .
Цитата(Grol @ 29.4.2006, 06:20 )
например превратить код Delphi в Asm.

Профиль
Группа: Модератор
Сообщений: 11360
Регистрация: 13.10.2004
Где: Питер

Репутация: 192
Всего: 483

Snowy
Дата 29.4.2006, 07:16 (ссылка) | (нет голосов) Загрузка .
Цитата(Grol @ 29.4.2006, 06:20 )
Может кто-то знает, как еще быстрее организовать эту функцию, например превратить код Delphi в Asm.
Код
procedure _EXP;
asm
< e**x = 2**(x*log2(e)) >
FLDL2E < y := x*log2e; >
FMUL
FLD ST(0) < i := round(y); >
FRNDINT
FSUB ST(1), ST < f := y - i; >
FXCH ST(1) < z := 2**f >
F2XM1
FLD1
FADD
FSCALE < result := z * 2**i >
FSTP ST(1)
end;

Профиль
Группа: Участник
Сообщений: 159
Регистрация: 5.5.2005
Где: Россия

Репутация: нет
Всего: нет

Grol
Дата 30.4.2006, 06:10 (ссылка) | (нет голосов) Загрузка .

Профиль
Группа: Участник
Сообщений: 159
Регистрация: 5.5.2005
Где: Россия

Репутация: нет
Всего: нет

Grol
Дата 30.4.2006, 06:35 (ссылка) | (нет голосов) Загрузка .

Профиль
Группа: Модератор
Сообщений: 11360
Регистрация: 13.10.2004
Где: Питер

Репутация: 192
Всего: 483

Snowy
Дата 30.4.2006, 10:57 (ссылка) | (нет голосов) Загрузка .
Цитата(Grol @ 30.4.2006, 06:10 )
И написанная мной функция вычисляется быстрей, чем стандартная Delphi, так как я не придерживаюсь большой точности.

Профиль
Группа: Участник
Сообщений: 159
Регистрация: 5.5.2005
Где: Россия

Репутация: нет
Всего: нет

Grol
Дата 30.4.2006, 11:02 (ссылка) | (нет голосов) Загрузка .

Профиль
Группа: Модератор
Сообщений: 11360
Регистрация: 13.10.2004
Где: Питер

Репутация: 192
Всего: 483

Snowy
Дата 30.4.2006, 11:20 (ссылка) | (нет голосов) Загрузка .

Профиль
Группа: Участник
Сообщений: 159
Регистрация: 5.5.2005
Где: Россия

Репутация: нет
Всего: нет

Grol
Дата 30.4.2006, 15:37 (ссылка) | (нет голосов) Загрузка .

Профиль
Группа: Участник
Сообщений: 39
Регистрация: 19.3.2006

Репутация: нет
Всего: нет

Wing
Дата 30.4.2006, 19:52 (ссылка) | (нет голосов) Загрузка .
Цитата(Grol @ 30.4.2006, 06:10 )
Но они вычисляются с большой точностью, а мне это не нужно, как я уже говорил в предыдущем моём посте.

Профиль
Группа: Участник
Сообщений: 129
Регистрация: 13.5.2005

Репутация: 2
Всего: 2

zalex
Дата 30.4.2006, 23:00 (ссылка) | (нет голосов) Загрузка .

Профиль
Группа: Участник
Сообщений: 3388
Регистрация: 12.3.2006
Где: Тосно

Репутация: 28
Всего: 89

Sunvas
Дата 1.5.2006, 00:19 (ссылка) | (нет голосов) Загрузка .
Цитата(zalex @ 30.4.2006, 23:00 )
Или дело в точности?

Профиль
Группа: Участник
Сообщений: 159
Регистрация: 5.5.2005
Где: Россия

Репутация: нет
Всего: нет

Grol
Дата 1.5.2006, 13:10 (ссылка) | (нет голосов) Загрузка .
Код
function ExpEx(StepenExp: Double): Double;
var
ExpX: Double;
begin
ExpX:=Sqr(StepenExp);
Result:=1+StepenExp+ExpX/2+(ExpX*StepenExp)/6+Sqr(ExpX)/24;
end;

А перед тем как использовать эту функцию просто необходимо определить знак степени, т.е. отрицательная степень или нет. Так как, если степень экспоненты отрицательная ряд сходится медленнно.

Код
If Stepen

Тогда больше мне уже ничего будет не нужно, т.к. я понимаю если это перевести в Asm, то работать это будет быстрее.

Это сообщение отредактировал(а) Grol — 1.5.2006, 13:11

Математические функции в Delphi

Занимаясь программированием, невозможно обойтись без использования математических функций. Большинство алгоритмических задач, вычисления требуют умения пользоваться ими. Конечно, некоторые из этих функций можно заменить какими-то наборами операторов, но такие решения всегда будут громоздкими, содержать дополнительные условия и выглядеть как изобретенный велосипед. Гораздо проще посвятить немного времени изучению готовых функций и разобраться в их использовании.

Для работы с этими функциями, в разделе описания uses нужно указать математическую библиотеку Math:

В таблице приведены наиболее часто используемые функции.

Обучающий курс. 12. Функции и процедуры в Delphi. Математические вычисления. Случайные числа

Сегодня мы поговорим о процедурах и функциях в Delphi . Что такое процедура? Это маленькая программа, выполняющая операции с указанными данными. Различают собственно процедуры и функции. Их основное отличие — процедура просто совершает какие-либо операции, а функция обязательно выдаёт какой-либо результат в результате своей работы. Существует огромное количество стандартных процедур и функций. Подпрограммы (так называют процедуры и функции) можно писать и самостоятельно, но об этом речь пойдёт позже. Сейчас нам нужно научиться работать с готовыми функциями.

Общие сведения о подпрограммах

Фактически, подпрограмма — это такая же полноценная программа, просто работает она не отдельно, не сама по себе, а включена в другую программу.
У подпрограммы всегда есть имя. Имя строится по тем же правилам, что и идентифмкатор. Как правило, имена даются вполне логичные. Например, если функция находит максимальное из нескольких чисел, то её логично назвать Max .
Подпрограммы могут иметь входные параметры. Входные параметры — это данные, которые сообщаются подпрограмме до начала её работы, а в процессе выполнения эти данные могут использоваться. Тем не менее, подпрограммы могут и не иметь входных параметров. Входные параметры также называют аргументами. Например, функции, которая узнаёт текущее время, никакие дополнительные параметры не нужны, а вот если функция считает факториал, то обязательно должно быть число, для которого он считается.
Как было сказано выше, функция выдаёт какое-то значение в результате своей работы. Процедура в общем случае значения не выдаёт.

Вызов подпрограмм

Вызываются подпрограммы по имени. Если подпрограмме требуется передать какие-либо параметры, то они указываются в скобках после имени подпрограммы через запятую. Если входные параметры отсутствуют, достаточно просто написать имя подпрограммы, либо оставить скобки пустыми. В случае, если работа происходит с функцией, результат можно «сохранить» в какой-то переменной, просто «присвоив» этой переменной функцию.

Обратите внимание: работа с функциями происходит как с обычными переменными, просто их значения вычисляются «на лету».

Функции математических вычислений

Эти функции работают с числовыми данными. Как правило, входным параметром является какое-то число, а выходным — результат вычисления. Практически везде аргумент является либо целым числом ( Integer ), либо вещественным ( Real ). Возвращаемое значение — тоже число. Рассмотрим некоторые из этих функций:

Abs(x) — модуль (абсолютное значение) указанного числа x . Пример: Abs(-5) = 5 .

Sin(x) — синус числа x . Здесь x — угол в радианах (не в градусах!). Пример: Sin(Pi/2) = 1 .

Cos(x) — косинус числа x . Аналогично, x — радианы. Пример: Cos(Pi) = -1 .

Exp(x) — экспонента, e x ( e в степени x ).

Ln(x) — натуральный логарифм числа x . Пример: Ln(Exp(2)) = 2 .

Sqr(x) — квадрат числа x ( x 2 ). Пример: Sqr(5) = 25 .

Sqrt(x) — квадратный корень числа x . Пример: Sqrt(64) = 8 .

Int(x) — целая часть числа x . Пример: Int(1.234) = 1 .

Frac(x) — дробная часть числа x . Пример: Frac(1.234) = 0.234 .

Round(x) — округление аргумента до ближайшего целого числа. Пример: Round(1.234) = 1 .

Trunc(x) — целая часть вещественного числа x. Пример: Trunc(1.234) = 1 .

Pred(x) — предыдущее значение x (например, для x = 2 это 1 ).

Succ(x) — следующее значение x (для x = 2 это 3 ).

Odd(x) — проверка аргумента на нечётность. Функция возвращает значение True , если аргумент является нечётным числом и False — если чётным. Пример: Odd(5) = True .

Предсказываю вопрос: в чём отличие Int() от Trunc() ? А отличие в том, что Int() возвращает число вещественного типа, а Trunc() — целочисленного .

Это лишь часть всех доступных функций. На самом деле их гораздо больше. Но помимо функций есть ещё процедуры.

Процедуры работы с числами

Поскольку процедуры в результате работы не выдают никакого значения, процедуры работы с числами просто изменяют переданные им параметры-переменные.

Inc(x) — увеличение аргумента на единицу. Фактически, это то же самое, что x:=x+1 . Тем не менее, рекомендуется использовать именно эту функцию, так как работает она быстрее.
Примечание: под понятием «быстрее» подразумевается, конечно, быстрота «компьютерная». Компьютер выполняет миллионы операций в секунду и для человека такие вещи незаметны.

Inc(x,n) — увеличение аргумента на число n . Эквивалентно записи x:=x+n .

На самом деле, это не две разные процедуры — просто параметр n является необязательным. Да, бывают необязательные параметры, которые можно указать, а можно и не указывать. Если они отсутствуют, то просто берётся какое-то значение по умолчанию. В данном случае n по умолчанию имеет значение 1 .

Dec(x,n) — уменьшение аргумента на n единиц. Точно также, как и в Inc , параметр n является необязательным. Эквивалентно записи x:=x-n .

В документации необязательные параметры обычно заключают в квадратные скобки, т.е. обычно пишут Inc(x , [n]) . Обратите внимание: это лишь условное обозначение, которое создано с целью узнавания, что параметр необязательный. В программном коде никаких скобок нет и быть не может.

Не хватает стандартных математических функций?

Существует дополнительный модуль с именем Math , в котором содержится большое число математических функций. Например, если нужно посчитать гиперболический арксеканс числа, то мучаться и описывать способ его вычисления вручную не придётся — есть готовая функция ArcSecH() .
Чтобы подключить модуль Math , откройте исходный код модуля. Для этого, когда открыта форма, следует нажать F12 , либо выбрать пункт меню View » Toggle Form/Unit . Далее нужно переместиться в самое начала модуля в раздел uses . В этом разделе через запятую описываются имена подключённых модулей. Как можно заметить, даже при наличии пустой формы несколько модулей уже подключены. В этот список и следует добавить Math :

Всё, теперь в Вашем распоряжении большое количество математических функций.

Пример комбинирования функций

Раз уж речь пошла о математических функциях, пусть пример будет на них и основан. Допустим, у нас есть такая сравнительно сложная функция:

Нам нужно создать программу, которая бы вычисляла значение этой функции по заданным числам x и y . Рассмотрим поэтапно элементы функции:
1) Возведение числа e в степень, модуль — функции Exp() и Abs() соответственно.
2) Натуральный логарифм — функция Ln() .
3) Число e . Часто спрашивают — как получить число e ? Ведь это, по сути, такая же константа, как и число пи . Но она не объявлена. А ответ прост: e = e 1 , поэтому e — это exp(1) .
4) Тангенс — функция Tan() .
Всё необходимое у нас есть, поэтому можно приступить к записи. Главное — не забывать заключать в скобки отдельные элементы формулы, чтобы порядок действий сохранился (в нашем примере это не потребуется).

Как возвести число в степень?

Почему я останавливаюсь на таких вопросах? Просто они очень часто возникают у новичков и не каждый может догадаться, как выполнить требуемую операцию.

Способ 1. X y можно преобразовать к виду e ln(x)⋅y . Тогда возведение в степень можно записать так:

Способ 2. В модуле Math есть функция для возведения в степень — Power . У функции 2 аргумента — основание и показатель степени. Запись, соответственно, следующая :=Power(x,y);

Случайные числа

Зачем нужны случайные числа? Как правило, чтобы проверить результаты какого-то эксперимента при различных условиях. На основе случайных чисел можно вычислять различные вероятности. Во всех языках программирования есть возможность использовать случайные числа.

В Pascal (и Delphi соответственно) случайные числа генерируются функцией Random . Функция принимает один параметр, да и тот необязательный. Этот параметр позволяет указать границу диапазона, из которого будет выбрано случайное число. Итак: Random([Range: Integer]) . Если Range указан, то число выбирается из диапазона 0 ( X — само случайное число, которое будет получено). Обратите внимание, что сама граница в диапазон не включается, т.е. Random(10) никогда не выдаст число 10 , хотя 0 — запросто. Если диапазон не указан, то он считается равным единице, т.е. 0 .

Пример. Создадим форму с кнопкой, но пусть кнопка каждую секунду изменяет своё положение. Воспользуемся таймером ( TTimer , вкладка System палитры компонент). Interval оставим без изменения ( 1 сек. ), а вот в обработчике запрограммируем произвольное изменение положения кнопки на форме. Разберёмся, что нам нужно:
1) Позиция кнопки на форме. Как Вы уже знаете, за положение отвечают свойства Left и Top , которые указывают положение левого верхнего угла кнопки относительно левого верхнего угла формы. Именно этим свойствам мы будем присваивать произвольные значения.
2) Каков будет диапазон для генерации случайных чисел? Очевидно, что кнопка не должна уйти за границы формы. Значит нам нужно подключить размеры самой формы, т.е. её высоту и ширину. В данном случае будем использовать не Width и Height , а ClientWidth и ClientHeight , так как в первые свойства входят заголовок и границы формы, а это лишние пиксели, за которые кнопка может вылезти. Однако и это ещё не всё — из этих размеров мы должны вычесть соответственно ширину и высоту самой кнопки, иначе она может частично скрыться за границами.
Пишем обработчик:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Button1.Left := Random(ClientWidth-Button1.Width);
Button1.Top := Random(ClientHeight-Button1.Height)
end ;

Запускаем программу и наблюдаем. Кнопка действительно прыгает с места на место. Но есть один крайне неприятный момент: запустите программу несколько раз и понаблюдайте за точками, в которые попадает кнопка. Вы сразу заметите, что каждый раз кнопка перемещается по одним и тем же точкам.

Отчего это происходит? Дело в том, что числа, выдаваемые функцией Random() на самом деле не являются случайными — они псевдослучайны , т.е. наблюдается повторение. К счастью, решение есть — специальная процедура Randomize() инициализирует генератор случайных чисел, который выдаёт действительно случайные числа. Вызвать эту процедуру нужно всего один раз за время работы программы — обычно это делается при запуске (например, в событии OnCreate формы). Процедура не принимает никаких параметров. Вернёмся к нашему примеру:

procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize
end ;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Button1.Left := Random(ClientWidth-Button1.Width);
Button1.Top := Random(ClientHeight-Button1.Height)
end ;

Теперь кнопка будет прыгать совершенно по разным точкам при каждом запуске программы.

Кстати, можно дописать скобки к названию процедуры — от этого работа не изменится: Randomize; = Randomize(); А запись немного красивее (на мой взгляд).

Дополнительные возможности редактора кода

В редакторе кода есть одна очень хорошая вещь — после того, как написано имя процедуры или функции и открыта скобка, появляется подсказка со всеми параметрами, которые подпрограмма принимает. Более того, там же указаны и типы данных всех параметров. Попробуйте, к примеру, набрать Inc( и подождать секунду — появится подсказка:

Как и было отмечено выше, второй параметр является необязательным и он заключен в квадратные скобки.

Такие подсказки позволяют моментально узнать, что подпрограмма требует на вход. При этом не придётся открывать модуль, где находится функция, либо искать её описание в документации или справочной системе.

Если функции или процедуре входные параметры не нужны, подсказка всё равно появится и сообщит об этом:

Если после набора имени и скобки подсказка не появилась, то и при компиляции программы скорее всего возникнет ошибка. Причиной, по которой компилятор не смог найти указанную функцию или процедуру, может быть ошибка при наборе имени, либо модуль, в котором описана подпрограмма, не подключен.
Ошибки при компиляции появляются внизу окна редактора кода с указанием номера строки, где обнаружена ошибка и описанием самой ошибки.
Попробуем запросить процедуру Randomiz (например, мы случайно недописали букву » e » на конце):

Строка с ошибкой выделилась, а внизу появился её номер ( 28 ) и описание — Undeclared identifier (неописанный идентификатор).

Заключение

В этом уроке охвачено сразу несколько тем — знакомство с процедурами и функциями, обзор математических функций и процедур, модуль Math, работа со случайными числами, а также удобства редактора кода и выделение ошибок при компиляции.

Ссылки по теме

Популярные статьи
Информационная безопасность Microsoft Офисное ПО Антивирусное ПО и защита от спама Eset Software


Бестселлеры
Курсы обучения «Atlassian JIRA — система управления проектами и задачами на предприятии»
Microsoft Office 365 для Дома 32-bit/x64. 5 ПК/Mac + 5 Планшетов + 5 Телефонов. Подписка на 1 год. Электронный ключ
Microsoft Windows 10 Профессиональная 32-bit/64-bit. Все языки. Электронный ключ
Microsoft Office для Дома и Учебы 2020. Все языки. Электронный ключ
Курс «Oracle. Программирование на SQL и PL/SQL»
Курс «Основы TOGAF® 9»
Microsoft Windows Professional 10 Sngl OLP 1 License No Level Legalization GetGenuine wCOA (FQC-09481)
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год. Электронный ключ
Windows Server 2020 Standard
Курс «Нотация BPMN 2.0. Ее использование для моделирования бизнес-процессов и их регламентации»
Антивирус ESET NOD32 Antivirus Business Edition
Corel CorelDRAW Home & Student Suite X8

О нас
Интернет-магазин ITShop.ru предлагает широкий спектр услуг информационных технологий и ПО.

На протяжении многих лет интернет-магазин предлагает товары и услуги, ориентированные на бизнес-пользователей и специалистов по информационным технологиям.

Хорошие отзывы постоянных клиентов и высокий уровень специалистов позволяет получить наивысший результат при совместной работе.

Создать мат функции быстрее, чем в Delphi?

Значит суть вопроса в следующем: В Delphi есть математические функции (Exp, Ln. ). Эти функции очень точно вычисляются и, если организовать цикл например на 100000 итераций, на каждой итерации будет вычисляться Exp(), то это будет достаточно длительно. Мне точность большая не нужна! И я написал функцию, которая вычисляет возведение экспоненты в степень, с небольшой точностью, но как показало время — она выполняется быстрее, чем через функцию Delphi Exp().
Вот эта функция:

Может кто-то знает, как еще быстрее организовать эту функцию, например превратить код Delphi в Asm.
А, да меня еще интересует функция Ln(), также как Exp(), как ее можно быстрее организовать, опять же мне большая точность не нужна.
Заранее большое всем спасибо за предоставленную информацию и ответы.

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки

Вход Регистрация Donate FAQ Правила Поиск

Быстрое вычисление функции exp(x)

На страницу Пред. 1 , 2 , 3 , 4
Печатать страницу | Печатать всю тему Пред. тема | След. тема
Экс-модератор

12/07/07
3706
Донецк, Украина

Последний раз редактировалось GAA 27.03.2014, 06:24, всего редактировалось 2 раз(а).

Экс-модератор

12/07/07
3706
Донецк, Украина

Последний раз редактировалось GAA 23.04.2020, 20:24, всего редактировалось 6 раз(а).
Исправлены опечатка и неточность

Посмотрел текcт ассемблерного листинга gcc в сообщении mustitz . В сообщении 840724 «с ходу» оптимизировал не очень: встроенные константы fpu желательно было хранить в стеке fpu, а не загружать в цикле. Кроме того, если допустить использование не только инструкций 386/387, но и Pentium II (если не путаю), то комбинацию
fucom
fstsw ax
sahf
следует заменить на
fcomi .

В результате к тексту в сообщении 840724 «добавилась» новая функция MainA4

У меня получилось, что разница во времени выполнения MainA2 и MainA4 около 3%. Выигрыш небольшой, но заметный. Преимущественно за счет хранения констант в стеке fpu.
mustitz , спасибо!

upd Компилировал для выполнения уже не в Delphi 5, а в Delphi 7.
[Встроенный ассемблер Delphi 5 поддерживает инструкции до Pentium I включительно, но не выше. В частности, fcomi он не поддерживает.]

Супермодератор

09/05/12
18978
Кронштадт

Экс-модератор

12/07/07
3706
Донецк, Украина

Последний раз редактировалось GAA 15.04.2014, 18:30, всего редактировалось 9 раз(а).

Нашёл по теме в сети (возможно, будет интересно):
1. Презентация “The VDT mathematical library: a modern reimplementation of Cephes” содержит сравнение скорости и точности вычисления элементарных функций различными библиотеками (VDT, SVML & VC). SVML (Short Vector Math Library, Intel)— самая быстрая, но без открытого кода и немного менее точная по сравнению с VDT.
Upd. Статья Hauth T., Innocente V., Piparo D. Development and Evaluation of Vectorised and Multi-Core Event Reconstruction Algorithms within the CMS Software Framework// J. Phys.: Conf. Ser. 396 052065.
2. Есть исходные тексты VDT на Си. (Прямые ссылки: Files\File list\math\vdt\include, Files\File list\math\vdt\tests.) [Но без SSE и AVX, или я не нашёл.]
На ассемблере На Си, но с SSE2 [тоже на базе Cephes, но не VDT]: Julien Pommier “Simple SSE and SSE2 (and now NEON) optimized sin, cos, log and exp”.

Исходя из текста в \inc\systemh.inc “win64 doesn’t support the legacy fpu”, для Win64 посмотрел не туда.

В system.inc находим

<$ifndef FPC_SYSTEM_HAS_EXP>
<$ifdef SUPPORT_DOUBLE>
<
This code was translated from uclib code, the original code
had the following copyright notice:

*
* ====================================================
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
* ====================================================
*>

<*
* Returns the exponential of x.
*
* Method
* 1. Argument reduction:
* Reduce x to an r so that |r| 7.09782712893383973096e+02 then exp(x) overflow
* if x
function fpc_exp_real ( d : ValReal ) : ValReal ; compilerproc ;
end ;

Текст функции

C1 = 6 . 9335937500000000000E — 1 ;
C2 = 2 . 1219444005469058277E — 4 ;
var n : Integer ;
px , qx , xx : Real ;
begin
if ( d > MAXLOG ) then
float_raise ( float_flag_overflow )
else
if ( d ) then
begin
float_raise ( float_flag_underflow ) ;
end
else
begin

px : = d * LOG2E ;
qx : = Trunc ( px + 0.5 ) ; < Trunc() truncates toward -infinity. >
n : = Trunc ( qx ) ;
d : = d — qx * C1 ;
d : = d + qx * C2 ;

< rational approximation for exponential >
< of the fractional part: >
< e**x - 1 = 2x P(x**2)/( Q(x**2) - P(x**2) ) >
xx : = d * d ;
px : = d * polevl ( xx , P , 2 ) ;
d : = px / ( polevl ( xx , Q , 3 ) — px ) ;
d : = ldexp ( d , 1 ) ;
d : = d + 1.0 ;
d : = ldexp ( d , n ) ;
result : = d ;
end ;
end ;

Экс-модератор

12/07/07
3706
Донецк, Украина

Последний раз редактировалось GAA 04.09.2014, 22:38, всего редактировалось 12 раз(а).
Исправлена часть грамматических ошибок

Аппроксимация Паде имеет вид отношения полиномов, которые могут вычисляться параллельно. Это наводит на мысль о плодотворности использования SSE при вычислении экспоненты с удвоенной точностью. Для проверки этого предположения и эффективности алгоритма, реализованного в cephes, я попробовал перевести функцию exp на ассемблер и сравнить скорость выполнения стандартной функции exp Delphi и написанной на ассемблере (Текст модуля для w32: Bmath.pas; проект, при помощи которого сравнивались времена выполнения: BMathTst.dpr; см. в конце сообщения в прикреплённом файле BMath.txt).

Результат тестирования: в Win32 использующая SSE процедура более чем в 1.5 раза медленней стандартной функции (FPU).

Для сравнения были использованы три компилятора:

    * Borland Delphi 7 (B7) [понимает инструкции до SSE2 включительно];
    * CodeGear Delphi for MS Windows 2007 (C2007) [до SSE3 включительно];
    * Embarcadero XE4, 21013 (XE4) [понимает SSE4].

При вызове функции exp (как и в случае некоторых других подпрограмм модуля system) B7 передаёт вещественный параметр через вершину стека FPU; а C2007 и XE4 — через программный стек (как и для любых подпрограмм). За исключением входа и выхода, код exp одинаков в библиотеках этих компиляторов. Т.е. выполнение system.exp B7 будет самым быстрым. Поэтому время выполнения цикла с этой (system.exp) функцией в скомпилированной B7 программе было выбрано за единицу.

Тестирование выполнялось на процессорах с архитектурами Penryn и Sandy Bridge.

В столбце B7 SSE2 FPU Scale приведен результат для масштабирования результата вычисления exp при помощи FPU (компиляция без определённой переменной CPU_SCALE). Изменение типа вызова функции приводит к её незначительному замедлению (1.02).
Удивительно, но использование FPU для масштабирования приносит существенное замедление.

Недостатками компиляторов B7 и C2007 являются:
1) невозможность задать 16-битное выравнивание данных (что приводит к необходимости вместо непосредственного смещения использовать адресацию по базе);
2) невозможность указать компилятору: в каком регистре возвращается результат, через какой регистр передаётся вещественный параметр.

Выравнивание есть в других компиляторах, однако использование базы не приводит к драматическому снижению производительности (проценты). [Регистров в данном случае хватает; не надо сохранять их в стеке и восстанавливать на выходе из процедуры.]

Невозможность приказать компилятору передавать параметр через xmm регистр тоже приводит к увеличению времени выполнения на проценты (менее чем в 1.1 раза). Также не приводит к существенным задержкам возврат через стек FPU (заодно можно выполнить масштабирование средствами fpu, или масштабировать переменную в памяти средствами cpu по ходу перебрасывании в fpu).

Другими словами, подгоняя под другие компиляторы эту ассемблерную процедуру, мы бы выиграли проценты.

Замечательно то, что с переходом к Sandy Br >заметно предпочтительней .

В случае с компиляцией 64-битных программ ситуация изменяется. Ряд компиляторов, в частности XE4, используют SSE для вещественной арифметики. Extended (long double) считается эквивалентным double. В подпрограмму вещественный параметр передаётся через xmm0, и через него же возвращается.

В файле BMath64.txt приведен модуль BMath64 (с функцией для 64-битного режима XE4) и проект BMathTst64 для тестирования. Функции проекта написаны на ассемблере для минимизации влияния окружения вызова функции на оценку скорости вычисления экспоненты. Тестирование выполнялось только на Sandy Bridge. Для того чтобы оценить плодотворность одновременного вычисления числителя и знаменателя в аппроксимации Паде тестировалось две модификации функции: в одной числитель и знаменатель вычислялись параллельно, в приводимой ниже таблице SSE P (от packed), а в другой — последовательно SSE S (от scalar).

Основанная на SSE и используемая по умолчанию в XE4 функция имеет приблизительно такую же скорость, что и стандартная функция, использующая FPU. Функция, основанная на аппроксимации Паде, заметно быстрее. Параллельное вычисление почти не даёт выигрыша во времени по сравнению со скалярным вариантом: слишком много скалярных вычислений + дополнительные действия для выполнения параллельного вычисления.

Upd. 16.04.2014 Я слегка оптимизировал код, добавил комментарии и скалярный вариант функции, а также отредактировал конец этого сообщения.

Upd2 4.05.2014 Для win64 (Embarcadero XE4) убрал сохранение регистров (см. начало post851310 ниже в теме), добавил вычисление функции от больших по модулю отрицательных значений аргумента (когда результат денормализованное число), а также генерацию исключительных ситуаций (Underflow и Overflow). В случае получения денормализованного результата время выполнения написанной реализации функция составляет приблизительно 0.10 от времени выполнения использующей FPU функции, а время выполнения стандартной SSE функции XE4 — приблизительно 0.6 от использующей FPU функции.
Медленное вычисление экспоненты (в случае больших отрицательных значений аргумента) функцией использующей FPU связано с медленным выполнением инструкции fscale для таких значений аргумента. Это очевидно, но, на всякий случай, для непосредственной проверки я написал простенькую программу (fscale.dpr) под Win32. При значениях аргумента -1070 или -1025 время выполнения приблизительно в 19 раз больше чем при 1 , -900 или -1025 .

program fscale ;
uses sysutils ;

const
n1 : Integer = 1 ;
n2 : Integer = — 900 ;
n3 : Integer = — 1020 ;
n4 : Integer = — 1025 ;
n5 : Integer = — 1070 ;
rep : LongWord = 500000000 ;
var
d : Double ;

procedure scale ;
asm
fldl2e
fscale
fstp st ( 1 )
end ;

Procedure MainF ( n : Integer ) ; stdcall ;
var i : LongWord ;
CW : Word ;
asm
mov ecx , [ rep ]
mov [ i ] , ecx
mov CW , 1372h
fldcw CW
@ loop :
fild dword ptr [ n ]
call scale
fstp qword ptr [ d ]
dec [ i ]
jnz @ loop
end ;

var Time1 , Time2 , d0 , d1 , d2 , d3 : TDateTime ;
f : text ;

begin
Assign ( f , ‘tstscale.txt’ ) ;
Append ( f ) ;
Writeln ( f ) ;
Writeln ( f , ‘rep=’ , rep ) ;

Time1 : = Time ;
MainF ( n1 ) ;
Time2 : = Time ;
d0 : = Time2 — Time1 ;
Writeln ( f , ‘n = ‘ , n1 : 7 , d0 * 24 * 60 * 60 : 10 : 4 ) ;

Time1 : = Time ;
MainF ( n2 ) ;
Time2 : = Time ;
d1 : = Time2 — Time1 ;
Writeln ( f , ‘n = ‘ , n2 : 7 , d1 * 24 * 60 * 60 : 10 : 4 ) ;
if d0 <> 0 then Writeln ( f , ‘FPU2/FPU1=’ , d1 / d0 : 7 : 3 ) ;

Time1 : = Time ;
MainF ( n3 ) ;
Time2 : = Time ;
d2 : = Time2 — Time1 ;
Writeln ( f , ‘n = ‘ , n3 : 7 , d2 * 24 * 60 * 60 : 10 : 4 ) ;
if d0 <> 0 then Writeln ( f , ‘FPU3/FPU1=’ , d2 / d0 : 7 : 3 ) ;

Time1 : = Time ;
MainF ( n4 ) ;
Time2 : = Time ;
d3 : = Time2 — Time1 ;
Writeln ( f , ‘n = ‘ , n4 : 7 , d3 * 24 * 60 * 60 : 10 : 4 ) ;
if d2 <> 0 then Writeln ( f , ‘FPU4/FPU1=’ , d3 / d2 : 7 : 3 ) ;

Time1 : = Time ;
MainF ( n5 ) ;
Time2 : = Time ;
d3 : = Time2 — Time1 ;
Writeln ( f , ‘n = ‘ , n5 : 7 , d3 * 24 * 60 * 60 : 10 : 4 ) ;
if d2 <> 0 then Writeln ( f , ‘FPU4/FPU1=’ , d3 / d2 : 7 : 3 ) ;

Вложения:
BMath64.txt [16.08 Кб]
Скачиваний: 85
BMath.txt [29.43 Кб]
Скачиваний: 37

Последний раз редактировалось pi-314 07.04.2014, 19:08, всего редактировалось 1 раз.

2 GAA:
Вот это research!

Большая просьба от тех, кто в asm полный ноль — выложите, пожалуйста, Ваш самый быстрый вариант на asm для случая единичного вычисления exp(), без нахождения суммы экспонент внутри функции.
Позаимствую для решаемой задачи!

Экс-модератор

12/07/07
3706
Донецк, Украина

Я добавил в сообщение post842741.html#p842741 исходный текст функций из RTL FPC [оказалось, что «всё в нём есть» :)] и отредактировал конец своего предыдущего сообщения. Попытка оптимизация не привела к заметным изменениям результата.

На мой взгляд, при сравнении скорости генерируемого различными компиляторами кода нужно учитывать следующие моменты.

1. Если нужны особо точные вычисления, то следует использовать 80-битный вещественный тип. Этот тип не поддерживается SSE. Следовательно, сравнивать имеет смысл только скорости использующего FPU кода. Delphi и, по-видимому, FPC генерируют такой код для win32, но не для win64. При этом необходимо удостовериться, что в FPU установлена точность вычислений 80 бит.

2. Если для расчётов достаточно 64-битного типа, то имеет смысл сравнивать код, который обеспечивает точность Double. В этом случае возможен как вариант с SSE, так и вариант FPU с установленной точностью 64-бита.
[Я для Win64 (Embarcadero XE4) специально сравнивал плохо сравнимые вещи: FPU с 80-битной точностью вычислений и SSE. Даже при таком «нечестном» сравнении FPU не проиграл (см. первый столбец табл. для Win64 в предыдущем сообщении). Можно предположить: в XE4 неудачный вариант (по умолчанию) функции для вычисления экспоненты (SSE). Но я сомневаюсь, что он очень уж сильно проигрывает функциям в библиотеках других компиляторов. Хотя немного проигрывать он вполне может. XE4, например, не поддерживает AVX. Да и вообще Delphi ещё со времён Borland не очень активно учитывает новые инструкции, особенно в функциях system.]

3. Если обеспечивать точность Double нет необходимости, то, очевидно в этом случае есть больше возможностей для манёвра и возможно заметное ускорение вычислений. В этом случае при сравнении хорошо бы указывать погрешность вычислений. Иначе возможно сравнение «несравнимого».

Экс-модератор

12/07/07
3706
Донецк, Украина

Последний раз редактировалось GAA 25.04.2014, 13:55, всего редактировалось 5 раз(а).

Т.е., если вдруг возникнет желание применять выложенную выше функцию для Win64, то надо проверить по используемой версии компилятора: скорее всего сохранение и восстановление xmm-регистров можно удалить. (На скорость вычислений это влияет крайне мало. Результаты тестов почти не изменятся.)

Там еще что-нибудь, возможно, надо доделать. Например, масштабирование при помощи SSE (а не CPU), генерацию исключительных ситуаций (это зависит от компилятора, и вообще, привязка к конкретному компилятору дело тонкое). В идеале скомпилировать на внешнем ассемблере и затем прилинковывать. Но тут уже начинается борьба за крохи. И тестировать надо, увы, старательно…

Как получены в cephes или Free Pascal выражения для коэффициентов многочленов P и Q , я не знаю, но можно было воспользоваться таким стандартным подходом.
Из определения гиперболического котангенса вытекает

где
Форма Паде имеет вид В формате с плавающей точкой значения коэффициентов будут
Q[0] = 2.0
Q[1] = 2.272727272727272727273e-1
Q[2] = 2.525252525252525252525e-3
Q[3] = 3.006253006253006253006e-6
P[0] = 1.0
P[1] = 3.03030303030303030303e-2
P[2] = 1.26262626262626262626e-4
P[3] = 0.0

Значения этих коэффициентов несколько отличаются от используемых в cephes или Free Pascal значений. Для проверки насколько они могут быть плохи, я выполнил следующее простенькое тестирование. Равномерно на генерировались 1000 миллионов значений и вычислялись: функция dexp, использующая приведенные выше в сообщении коэффициенты, и стандартная функция exp, использующая FPU. Максимальная разница в возвращаемых значениях составляла 2 двоичных знака, максимальная погрешность — приближенно . Такое же расхождение в количестве двоичных знаков (и максимальная погрешность) было и для значений из cephes (но при другом значении аргумента).

Экс-модератор

12/07/07
3706
Донецк, Украина

Последний раз редактировалось GAA 17.11.2015, 13:49, всего редактировалось 7 раз(а).

Конечно, лучше выбирать язык и компилятор более подходящий для научных расчетов. Но, если такой возможности нет, то возникает желание написать заплатку на ассемблере. Чтобы оценить выигрыш от такой заплатки я написал черновики с использованием SSE 4.2.

При помощи директив условной компиляции (УК) подпрограммы могут быть скомпилированы:
(a) с проверкой аргументов на попадание в диапазон (переменная ExpRangeCheck) или без оной [далее кратко: R+ или R-];
(b) с модификацией без сохранения значений регистров xmm0—xmm3 (официальное соглашение: переменная УК DelphiRSC) или с модификацией регистров xmm0—xmm5;
(с) с масштабированием денормализованных результатов при помощи добавления подразумеваемой целой 1 и сдвига (переменная УК PsrlqScalling), или при помощи добавления фиксированного значения к показателю и умножения для компенсации этого добавления [далее кратко: PS и MS];
(d) c возможностью генерации исключительных ситуаций (переменная УК RaiseException) или без оной [далее кратко: RE+ или RE-].

Несмотря на то, что в документации Embarcadero указывается: без сохранения значений можно модифицировать только xmm0—xmm3, в библиотечных функциях они сами [разработчики Embarcadero] не сохраняют значения xmm4 и xmm5, а директивы .SAVENV xmm4 и .SAVENV xmm5 не приводят к формированию в прологе и эпилоге кода. Поэтому в приводимых ниже тестах программа была скомпилирована без определенной переменной DelphiRSC. Также в реализациях на асме не контролировались исключения «неточной операции» (“Precision Error”) и «денормализованный результат».

1. В прикреплении DPExp64.txt подпрограммы основаны на реализованном в cephes на С алгоритме, базирующемся на Паде аппроксимации.

Модуль DPExp64.pas содержит три подпрограммы:
pexpsd(x: Double): Double — для вычисления экспоненты от одного аргумента (это слегка доработанная и переименованная функция dexp из приводившегося выше в теме модуля BMath64);
pexppsd(x1, x2: Double, out d: TDD) — процедура для вычисления за один вызов значений для двух аргументов, использующая при необходимости функцию pexpsd;
pexppd(x1, x2: Double, out d: TDD) — аналогичная процедура вычисления за один вызов двух значений, но не использующая pexpsd.

Сравнение скоростей выполнения проводилось на процессоре с архитектурой Sandy Br >exp модуля system XE4. Т.е. выигрыш от вычисления за один вызов двух значений — мал.

2. В прикреплении DDExp64.txt две функции и процедура для вычисления экспоненты с погрешностью не больше одного бита в последнем знаке мантиссы :
expsd_Tang (x: Double): Double — реализация алгоритма из работы Tang P.T.P. Table-Driven Implementation of the Exponential Function in IEEE Floating-Point Arithmetic // ACM Transactions on Mathematical Software, Vol. 15, Issue 2, P. 144–157 (1989);
expsd (x: Double): Double — модификация алгоритма SunPro, 1993 г.
exppd (x1, x2: Double; d: TDD) — процедура вычисления за один вызов двух экспонент, «векторизация» функции expsd.

Скорость expsd_Tang оказалась не выше, чем у expsd. В модификации R+, RE+ время выполнения двух вызовов expsd составляет приблизительно 0.69, а одного вызова exppd — 0.46 от времени выполнения двух вызовов стандартной функции exp модуля system XE4. При включении возможности генерации исключений, выигрыш от использования «приближенной» процедуры pexppd вместо «точной» exppd становится мизерным.

3. В качестве примера использования pexpsd и exppd был взят слегка изменённый пример «с двумя экспонентами» из сообщения post840657.html#p840657 pi-314 (Он не вполне осмысленный, ну да ладно.) В конце сообщения приведен проект тестирования.
Для случая R-, PS, RE- время выполнения с использованием pexpsd составляло 0.35 от времени выполнения с использованием стандартной функции exp, а время выполнения с использованием exppd — 0.30. Т.е. скорость выполнения возросла в 2.88 и 3.34 раза соответственно.
Для случая R+, PS, RE+ время составило 0.35 и 0.31, т.е. добавление проверки на попадание в диапазон и возможности генерации исключений практически не замедлило выполнение.

Итого. «Векторизация», использование SSE4.2 вместо SSE2 (Embarcadero) и не отслеживание «неточный результат» и денормализованный операнд позволило значительно ускорить выполнение для большинства значений аргументов. По тексту подпрограмм встречаются места, в которых полезно использовать «неразрушающие возможности» AVX. Т.е. использование AVX позволит немного увеличить скорость выполнения даже при вычислении двух значений за один вызов процедуры.

Пожелания и вопрос. Помимо очевидного «Буду рад всем конструктивным замечаниям по тексту подпрограмм», есть пожелания и вопрос.

Я хотел выполнить сравнение и для Visual C++, но времени не оказалось. В тексте достаточно комментариев. На мой взгляд, его очень быстро можно перенести на другой 64-битный компилятор c SSE «соглашением» о передаче параметров и возврате результата функциями. Интересно было бы посмотреть на результаты сравнения скорости написанных функций и, например, используемых Visual C++.

Кроме двух указанных мною в теме, я не нашел полезных ссылок на посвященные вычислению exp статьи с алгоритмами удобными для реализации с использованием SSE или AVX . Если такие ссылки имеются, то приведите, пожалуйста, в ветке.

64-битный компилятор Embarcadero XE4 неправильно генерирует код для перемещения учетверённого слова из xmm-регистра в регистр общего назначения и наоборот. Факт известный (см., например, QualityCentral/Delphi-BCB/Compiler/Delphi/BASM/Report 112094). Это обоснованная особенность или откровенный баг? Если баг, то исправили ли его в последней версии? И как обстоят дела с этим в других компиляторах?

function stdTst : Double ;
const
Delta : Double = 0.0000001 ;
var
x : Double ;
Sum : Double ;
begin
x : = — 10.0 ;
Sum : = 0.0 ;
repeat
if X > 10.0 then Break ;
Sum : = Sum + exp ( x ) * Delta / exp ( 10 * x ) ;
x : = x + Delta ;
until False ;
Result : = Sum ;
end ;

function pexpTst : Double ;
const
Delta : Double = 0.0000001 ;
var
x : Double ;
Sum : Double ;
begin
x : = — 10.0 ;
Sum : = 0.0 ;
repeat
if x > 10.0 then Break ;
Sum : = Sum + pexpsd ( x ) * Delta / pexpsd ( 10 * x ) ;
x : = x + Delta ;
until False ;
Result : = Sum ;
end ;

function exppdTst : Double ;
const
Delta : Double = 0.0000001 ;
var
x : Double ;
Sum : Double ;
d : DDExp64 . TDD ;
begin
x : = — 10.0 ;
Sum : = 0.0 ;
repeat
if x > 10.0 then Break ;
exppd ( x , 10 * x , d ) ;
Sum : = Sum + d . Lo * Delta / d . Hi ;
x : = x + Delta ;
until False ;
Result : = Sum ;
end ;

var Time1 , Time2 , d1 , d2 , d3 : TDateTime ;
f : text ;
begin
Assign ( f , ‘PITst.txt’ ) ;
Append ( f ) ;
Writeln ( f , SS ) ;
Time1 : = Time ;
StdTst ;
Time2 : = Time ;
d1 : = Time2 — Time1 ;
Writeln ( f , ‘ S=’ , d1 * 24 * 60 * 60 : 10 : 3 ) ;

Time1 : = Time ;
pexpTst ;
Time2 : = Time ;
d2 : = Time2 — Time1 ;
Writeln ( f , ‘ P=’ , d2 * 24 * 60 * 60 : 10 : 3 , ‘ P/S =’ , d2 / d1 : 4 : 2 , ‘ S/P =’ , d1 / d2 : 4 : 2 ) ;

Time1 : = Time ;
exppdTst ;
Time2 : = Time ;
d3 : = Time2 — Time1 ;
Writeln ( f , ‘ D=’ , d3 * 24 * 60 * 60 : 10 : 3 , ‘ D/S =’ , d3 / d1 : 4 : 2 , ‘ S/D =’ , d1 / d3 : 4 : 2 ) ;

Вложения:
Комментарий к файлу: Модуль DPExp64.pas и проект для тестирования скоростей выполнения (Embarcadero Delphi XE4 64-bit)
DPExp64.txt [53.15 Кб]
Скачиваний: 56
Комментарий к файлу: Модуль DDExp64.pas и проект для тестирования скоростей выполнения (Embarcadero Delphi XE4 64-bit)
DDExp64.txt [45.64 Кб]
Скачиваний: 39
Страница 4 из 4 [ Сообщений: 55 ] На страницу Пред. 1 , 2 , 3 , 4

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей

Обучающий курс. 12. Функции и процедуры в Delphi. Математические вычисления. Случайные числа

Сегодня мы поговорим о процедурах и функциях в Delphi . Что такое процедура? Это маленькая программа, выполняющая операции с указанными данными. Различают собственно процедуры и функции. Их основное отличие — процедура просто совершает какие-либо операции, а функция обязательно выдаёт какой-либо результат в результате своей работы. Существует огромное количество стандартных процедур и функций. Подпрограммы (так называют процедуры и функции) можно писать и самостоятельно, но об этом речь пойдёт позже. Сейчас нам нужно научиться работать с готовыми функциями.

Общие сведения о подпрограммах

Фактически, подпрограмма — это такая же полноценная программа, просто работает она не отдельно, не сама по себе, а включена в другую программу.
У подпрограммы всегда есть имя. Имя строится по тем же правилам, что и идентифмкатор. Как правило, имена даются вполне логичные. Например, если функция находит максимальное из нескольких чисел, то её логично назвать Max .
Подпрограммы могут иметь входные параметры. Входные параметры — это данные, которые сообщаются подпрограмме до начала её работы, а в процессе выполнения эти данные могут использоваться. Тем не менее, подпрограммы могут и не иметь входных параметров. Входные параметры также называют аргументами. Например, функции, которая узнаёт текущее время, никакие дополнительные параметры не нужны, а вот если функция считает факториал, то обязательно должно быть число, для которого он считается.
Как было сказано выше, функция выдаёт какое-то значение в результате своей работы. Процедура в общем случае значения не выдаёт.

Вызов подпрограмм

Вызываются подпрограммы по имени. Если подпрограмме требуется передать какие-либо параметры, то они указываются в скобках после имени подпрограммы через запятую. Если входные параметры отсутствуют, достаточно просто написать имя подпрограммы, либо оставить скобки пустыми. В случае, если работа происходит с функцией, результат можно «сохранить» в какой-то переменной, просто «присвоив» этой переменной функцию.

Обратите внимание: работа с функциями происходит как с обычными переменными, просто их значения вычисляются «на лету».

Функции математических вычислений

Эти функции работают с числовыми данными. Как правило, входным параметром является какое-то число, а выходным — результат вычисления. Практически везде аргумент является либо целым числом ( Integer ), либо вещественным ( Real ). Возвращаемое значение — тоже число. Рассмотрим некоторые из этих функций:

Abs(x) — модуль (абсолютное значение) указанного числа x . Пример: Abs(-5) = 5 .

Sin(x) — синус числа x . Здесь x — угол в радианах (не в градусах!). Пример: Sin(Pi/2) = 1 .

Cos(x) — косинус числа x . Аналогично, x — радианы. Пример: Cos(Pi) = -1 .

Exp(x) — экспонента, e x ( e в степени x ).

Ln(x) — натуральный логарифм числа x . Пример: Ln(Exp(2)) = 2 .

Sqr(x) — квадрат числа x ( x 2 ). Пример: Sqr(5) = 25 .

Sqrt(x) — квадратный корень числа x . Пример: Sqrt(64) = 8 .

Int(x) — целая часть числа x . Пример: Int(1.234) = 1 .

Frac(x) — дробная часть числа x . Пример: Frac(1.234) = 0.234 .

Round(x) — округление аргумента до ближайшего целого числа. Пример: Round(1.234) = 1 .

Trunc(x) — целая часть вещественного числа x. Пример: Trunc(1.234) = 1 .

Pred(x) — предыдущее значение x (например, для x = 2 это 1 ).

Succ(x) — следующее значение x (для x = 2 это 3 ).

Odd(x) — проверка аргумента на нечётность. Функция возвращает значение True , если аргумент является нечётным числом и False — если чётным. Пример: Odd(5) = True .

Предсказываю вопрос: в чём отличие Int() от Trunc() ? А отличие в том, что Int() возвращает число вещественного типа, а Trunc() — целочисленного .

Это лишь часть всех доступных функций. На самом деле их гораздо больше. Но помимо функций есть ещё процедуры.

Процедуры работы с числами

Поскольку процедуры в результате работы не выдают никакого значения, процедуры работы с числами просто изменяют переданные им параметры-переменные.

Inc(x) — увеличение аргумента на единицу. Фактически, это то же самое, что x:=x+1 . Тем не менее, рекомендуется использовать именно эту функцию, так как работает она быстрее.
Примечание: под понятием «быстрее» подразумевается, конечно, быстрота «компьютерная». Компьютер выполняет миллионы операций в секунду и для человека такие вещи незаметны.

Inc(x,n) — увеличение аргумента на число n . Эквивалентно записи x:=x+n .

На самом деле, это не две разные процедуры — просто параметр n является необязательным. Да, бывают необязательные параметры, которые можно указать, а можно и не указывать. Если они отсутствуют, то просто берётся какое-то значение по умолчанию. В данном случае n по умолчанию имеет значение 1 .

Dec(x,n) — уменьшение аргумента на n единиц. Точно также, как и в Inc , параметр n является необязательным. Эквивалентно записи x:=x-n .

В документации необязательные параметры обычно заключают в квадратные скобки, т.е. обычно пишут Inc(x , [n]) . Обратите внимание: это лишь условное обозначение, которое создано с целью узнавания, что параметр необязательный. В программном коде никаких скобок нет и быть не может.

Не хватает стандартных математических функций?

Существует дополнительный модуль с именем Math , в котором содержится большое число математических функций. Например, если нужно посчитать гиперболический арксеканс числа, то мучаться и описывать способ его вычисления вручную не придётся — есть готовая функция ArcSecH() .
Чтобы подключить модуль Math , откройте исходный код модуля. Для этого, когда открыта форма, следует нажать F12 , либо выбрать пункт меню View » Toggle Form/Unit . Далее нужно переместиться в самое начала модуля в раздел uses . В этом разделе через запятую описываются имена подключённых модулей. Как можно заметить, даже при наличии пустой формы несколько модулей уже подключены. В этот список и следует добавить Math :

Всё, теперь в Вашем распоряжении большое количество математических функций.

Пример комбинирования функций

Раз уж речь пошла о математических функциях, пусть пример будет на них и основан. Допустим, у нас есть такая сравнительно сложная функция:

Нам нужно создать программу, которая бы вычисляла значение этой функции по заданным числам x и y . Рассмотрим поэтапно элементы функции:
1) Возведение числа e в степень, модуль — функции Exp() и Abs() соответственно.
2) Натуральный логарифм — функция Ln() .
3) Число e . Часто спрашивают — как получить число e ? Ведь это, по сути, такая же константа, как и число пи . Но она не объявлена. А ответ прост: e = e 1 , поэтому e — это exp(1) .
4) Тангенс — функция Tan() .
Всё необходимое у нас есть, поэтому можно приступить к записи. Главное — не забывать заключать в скобки отдельные элементы формулы, чтобы порядок действий сохранился (в нашем примере это не потребуется).

Как возвести число в степень?

Почему я останавливаюсь на таких вопросах? Просто они очень часто возникают у новичков и не каждый может догадаться, как выполнить требуемую операцию.

Способ 1. X y можно преобразовать к виду e ln(x)⋅y . Тогда возведение в степень можно записать так:

Способ 2. В модуле Math есть функция для возведения в степень — Power . У функции 2 аргумента — основание и показатель степени. Запись, соответственно, следующая :=Power(x,y);

Случайные числа

Зачем нужны случайные числа? Как правило, чтобы проверить результаты какого-то эксперимента при различных условиях. На основе случайных чисел можно вычислять различные вероятности. Во всех языках программирования есть возможность использовать случайные числа.

В Pascal (и Delphi соответственно) случайные числа генерируются функцией Random . Функция принимает один параметр, да и тот необязательный. Этот параметр позволяет указать границу диапазона, из которого будет выбрано случайное число. Итак: Random([Range: Integer]) . Если Range указан, то число выбирается из диапазона 0 ( X — само случайное число, которое будет получено). Обратите внимание, что сама граница в диапазон не включается, т.е. Random(10) никогда не выдаст число 10 , хотя 0 — запросто. Если диапазон не указан, то он считается равным единице, т.е. 0 .

Пример. Создадим форму с кнопкой, но пусть кнопка каждую секунду изменяет своё положение. Воспользуемся таймером ( TTimer , вкладка System палитры компонент). Interval оставим без изменения ( 1 сек. ), а вот в обработчике запрограммируем произвольное изменение положения кнопки на форме. Разберёмся, что нам нужно:
1) Позиция кнопки на форме. Как Вы уже знаете, за положение отвечают свойства Left и Top , которые указывают положение левого верхнего угла кнопки относительно левого верхнего угла формы. Именно этим свойствам мы будем присваивать произвольные значения.
2) Каков будет диапазон для генерации случайных чисел? Очевидно, что кнопка не должна уйти за границы формы. Значит нам нужно подключить размеры самой формы, т.е. её высоту и ширину. В данном случае будем использовать не Width и Height , а ClientWidth и ClientHeight , так как в первые свойства входят заголовок и границы формы, а это лишние пиксели, за которые кнопка может вылезти. Однако и это ещё не всё — из этих размеров мы должны вычесть соответственно ширину и высоту самой кнопки, иначе она может частично скрыться за границами.
Пишем обработчик:

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Button1.Left := Random(ClientWidth-Button1.Width);
Button1.Top := Random(ClientHeight-Button1.Height)
end ;

Запускаем программу и наблюдаем. Кнопка действительно прыгает с места на место. Но есть один крайне неприятный момент: запустите программу несколько раз и понаблюдайте за точками, в которые попадает кнопка. Вы сразу заметите, что каждый раз кнопка перемещается по одним и тем же точкам.

Отчего это происходит? Дело в том, что числа, выдаваемые функцией Random() на самом деле не являются случайными — они псевдослучайны , т.е. наблюдается повторение. К счастью, решение есть — специальная процедура Randomize() инициализирует генератор случайных чисел, который выдаёт действительно случайные числа. Вызвать эту процедуру нужно всего один раз за время работы программы — обычно это делается при запуске (например, в событии OnCreate формы). Процедура не принимает никаких параметров. Вернёмся к нашему примеру:

procedure TForm1.FormCreate(Sender: TObject);
begin
Randomize
end ;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
Button1.Left := Random(ClientWidth-Button1.Width);
Button1.Top := Random(ClientHeight-Button1.Height)
end ;

Теперь кнопка будет прыгать совершенно по разным точкам при каждом запуске программы.

Кстати, можно дописать скобки к названию процедуры — от этого работа не изменится: Randomize; = Randomize(); А запись немного красивее (на мой взгляд).

Дополнительные возможности редактора кода

В редакторе кода есть одна очень хорошая вещь — после того, как написано имя процедуры или функции и открыта скобка, появляется подсказка со всеми параметрами, которые подпрограмма принимает. Более того, там же указаны и типы данных всех параметров. Попробуйте, к примеру, набрать Inc( и подождать секунду — появится подсказка:

Как и было отмечено выше, второй параметр является необязательным и он заключен в квадратные скобки.

Такие подсказки позволяют моментально узнать, что подпрограмма требует на вход. При этом не придётся открывать модуль, где находится функция, либо искать её описание в документации или справочной системе.

Если функции или процедуре входные параметры не нужны, подсказка всё равно появится и сообщит об этом:

Если после набора имени и скобки подсказка не появилась, то и при компиляции программы скорее всего возникнет ошибка. Причиной, по которой компилятор не смог найти указанную функцию или процедуру, может быть ошибка при наборе имени, либо модуль, в котором описана подпрограмма, не подключен.
Ошибки при компиляции появляются внизу окна редактора кода с указанием номера строки, где обнаружена ошибка и описанием самой ошибки.
Попробуем запросить процедуру Randomiz (например, мы случайно недописали букву » e » на конце):

Строка с ошибкой выделилась, а внизу появился её номер ( 28 ) и описание — Undeclared identifier (неописанный идентификатор).

Заключение

В этом уроке охвачено сразу несколько тем — знакомство с процедурами и функциями, обзор математических функций и процедур, модуль Math, работа со случайными числами, а также удобства редактора кода и выделение ошибок при компиляции.

Exp — Функция Delphi

Имеющее множество достоинств и довольно популярное средство разработки Delphi позволяет расширять функциональные возможности среды разработчика. Речь идет не о косметических изменениях в интерфейсе и не о добавлении компонентов или их наборов, а о придании рабочему окружению программиста новых полезных возможностей, не предусмотренных его создателями. Для решения подобной задачи в Delphi можно воспользоваться так называемыми экспертами. Вспомните, как удобно, ответив на несколько вопросов, создать готовую форму для вывода содержимого таблиц. Или, щелкнув мышью на нужном элементе в списке New Items, получить “костяк” вашего будущего проекта (рис. 1).

Рис. 1. Многие возможности Delphi реализуются с помощью экспертов

Что это — стандартные возможности рабочей среды? Да, но применить их можно лишь с помощью эксперта. О том, как это сделать, и пойдет речь далее.

Эксперты в Delphi — что это такое?

Если не хватает возможностей среды или какие-то операции кажутся слишком громоздкими, то эксперты — именно то, что нужно. С помощью экспертов вы словно проникаете внутрь среды Delphi и без труда дополняете ее. Естественно, такое проникновение должно быть осторожным и аккуратным, потому как неправильное обращение с объектами и интерфейсами может вызвать сбои в работе среды или даже ее разрушение. Эксперты могут существовать в виде библиотек DLL либо компилированных модулей DCU. Выбор “формы жизни” будущего эксперта остается за вами, но имейте в виду, что расширение файла эксперта определяет способ его регистрации. О способах регистрации чуть далее — сперва давайте рассмотрим стили экспертов Delphi. Их всего четыре, и все они приведены в таблице.

Стили экспертов


Стандартный Добавить в подменю Help пункт меню Надстройки

То же в определенное экспертом подменю
пункт меню Формы — // — во вкладку Forms диалогового окна New
Items пиктограмму эксперта Проекта — // — во вкладку Projects диалогового окна New
Items то же

Главное отличие между стилями заключается в способе вызова эксперта пользователем в среде Delphi. Как видите, можно определить самый удобный из них. Реализация экспертов предполагает использование интерфейса Open Tools API — набора классов, позволяющего обращаться ко множеству функций среды Delphi. В экспертах Open Tools API может использоваться для:

  • получения информации о проекте;
  • получения информации о модуле или форме;
  • управления модулями проекта (дл открытия, закрытия, добавления, создания);
  • управления ресурсами проекта;
  • модификации меню Delphi;
  • регистрации изменений в проекте;
  • регистрации изменений в модуле.

Следует заметить, что интерфейс Open Tools API доступен только из программ, запущенных как часть интегрированной среды Delphi. В следующем разделе мы рассмотрим несколько полезных экспертов.

Реализация класса TIExpert

Для создания нового эксперта необходимо наследовать новый класс от класса TIExpert, переопределив при этом часть его методов (таблица 2)

Возможность переопределени методов экспертов тех или иных стилей

Все девять методов (таблица 3) предоставляют информацию об эксперте и организуют его взаимодействие со средой. TIExpert — это абстрактный виртуальный класс с заданными, но не реализованными функциональными возможностями. От этого класса будут порождены другие, имеющие необходимые возможности.

Определение класса TIExpert приведено далее.

TIExpert = class(TInterface)
public
< Методы пользовательского
интерфейса с экспертом >
function GetName: string;
virtual; stdcall; abstract;
function GetAuthor: string;
virtual; stdcall; abstract;
function GetComment: string;
virtual; stdcall; abstract;
function GetPage: string;
virtual; stdcall; abstract;
function GetGlyph: HICON;
virtual; stdcall; abstract;
function GetStyle:
TExpertStyle; virtual; stdcall;
abstract;
function GetState:
TExpertState; virtual; stdcall;
abstract;
function GetIDString: string;
virtual; stdcall; abstract;
function GetMenuText: string;
virtual; stdcall; abstract;
< Запуск эксперта >
procedure Execute; virtual;
stdcall; abstract;
end;

Open Tools API

Open Tools API — это набор интерфейсов; они предоставляют доступ к среде Delphi и позволяют управлять файлами и проектами. Основной объект Open Tools API — ToolServices — это глобальная переменная. При запуске Delphi создается экземпляр класса TIToolServices, и переменной ToolServices присваивается ссылка на него. Эксперты могут использовать ToolServices для доступа к функциям среды разработки.

Любой сервис, предоставляемый Open Tools API, прямо или косвенно вызывается через ToolServices. В таблице приведено краткое описание Open Tools API.

Переопределение методов — задача довольно простая; она предполагает написание всего нескольких строк кода. Например, реализация метода GetStyle вряд ли отнимет у вас много времени:

function MyExpert.GetStyle:
TexpertStyle
begin
Result := [esStandard];
end;

Регистрация экспертов

Зарегистрировать эксперт можно одним из двух способов. Первый способ сводится к определению эксперта как компонента путем вызова процедуры RegisterLibraryExpert из процедуры Register. Второй способ заключается в создании DLL-библиотеки эксперта. Преимущество первого способа в том, что не приходитс закрывать среду Delphi при внесении изменений в эксперт — достаточно его перекомпилировать. Сперва рассмотрим регистрацию эксперта как компонента. Необходимо добавить в модуль эксперта процедуру Register:

Procedure Register;
Implementation <$R*.DFM>
Procedure Register;
Begin
RegisterLibraryExpert
(TPowerExpert. Create);
// TpowerExpert — это класс регистрируемого эксперта
End;

Для регистрации эксперта как DLLбиблиотеки следует выполнить две операции: реализовать новый проект DLL и изменить содержимое системного реестра Windows. Итак, создаем DLL. Выполните команду File р New, а затем укажите Delphi, что необходимо создать DLL. В результате появится новое окно модуля с неким набором исходного кода. После этого следует экспортировать функцию InitExpert. Обратите внимание, что эта функция экспортируется с помощью специальной константы ExpertEntryPoint, которую Delphi определяет для всех экспертов, создаваемых в виде DLL. Основное назначение функции InitExpert — возврат ссылки на объект ToolServices для дальнейшего использования и вызова процедуры RegisterProc, которая, собственно, и регистрирует эксперт. Ниже приведена реализация этой функции:

Function InitExpert(
ToolServices:ToolServices;
RegisterProc:TexpertRegisterProc;
var
Terminate:TExpertTerminateProc):
Boolean; export; stdcall;
implementation
procedure TerminateExpert;
begin
// завершение работы эксперта
end;
function InitExpert(
ToolServices:ToolServices;
RegisterProc:TExpertRegisterProc;
var
Terminate:TExpertTerminateProc):
Boolean; export; stdcall;
begin
Result:=False;
end;
// проверка, является ли запущенное приложение единственным
if (ToolServices=nil) or Assigned(ExptIntf.ToolServices)
then Exit;
ExptIntf.ToolServices:=ToolServices;
//сохраняем указатель на ToolServices
Application.Handle:=
ToolServices.GetParentHandle;
//сохраняем указатель на
ToolServices для родительского
окна
Terminate:=TerminateExpert;
//устанавливаем процедуру завершения
RegisterProc(TGenericExpert.Create);
//регистрация эксперта
Result:=True;
end;

Когда DLL с экспертом будет готова, от вас потребуется лишь изменить системный реестр так, чтобы Delphi “знала” расположение библиотеки с экспертом и смогла ее загрузить. Для этого с помощью редактора реестра (regedit.exe) добавьте в реестр такую запись:

HKEY_CURRENT_USER\Software\Borland\ Delphi\4.0\Experts
MyExpert=C:\MyExpertts\MyExpert.DLL

Для того чтобы среда зарегистрировала DLL, Delphi необходимо перезапустить. Вариант реализации эксперта в виде DLL кажетс автору менее удобным: перезагрузка среды отнимает больше времени по сравнению с перекомпиляцией библиотеки компонентов, что особенно ощутимо при отладке эксперта. Еще одна проблема — неполна совместимость экспертов в виде DLL, которые были созданы и скомпилированы для других версий Delphi. Автор надеется, что эта стать поможет профессионалам поближе познакомиться с экспертами Delphi. Возможно, ее публикация подтолкнет многих программистов к изучению темы.

Некоторые полезные эксперты

Знаете ли вы, что в Internet есть предостаточно мест, где можно найти эксперты для Delphi. Одно из таких мест — польский сервер “Delphi Super Page” (http://delphi.icm.edu.pl/). Там вы найдете множество различных экспертов и полезных компонентов. Давайте рассмотрим самый интересный, по мнению автора, набор экспертов, предоставляющий возможность ускорить разработку приложений на Delphi. Его можно загрузить по адресу: http://delphi.icm.edu.pl/ftp/d40free/myexp100.zip.

Рассмотрим вкратце эти маленькие “добавки”. Набор содержит эксперт — редактор префиксов для имен компонентов. После того, как он будет установлен в инспекторе объектов, напротив свойства Name появится кнопка с многоточием. Это говорит о том, что можно воспользоваться редактором для изменения свойства Name. С его помощью можно указывать префикс для данного класса компонента. Строго говоря, использование префиксов в названиях компонентов — это правило хорошего тона. В меню Tools теперь появляется новое подменю Prefix list editor, с помощью которого можно изменять и добавлять такие префиксы.

Как известно, некоторые компоненты являются контейнерами для других (например, TPanel, TGroupBox, TScrollBox и т. п.). Установленный набор позволит управлять выравниванием дочерних компонентов. Для этого достаточно щелкнуть правой кнопкой мыши и выбрать в контекстном меню пункт Align controls. В Delphi есть мастер создания элементов управления, работающих с данными.

Однако в рассматриваемом наборе имеется эксперт, благодаря которому можно создавать компоненты для работы с данными более совершенным способом. С помощью эксперта, вызываемого командой Tools р Shortcut list editor, можно определить свой набор клавиатурных эквивалентов для главного меню Delphi. Кроме всего прочего, после установки набора вы обнаружите, что палитра компонентов Delphi стала многострочной (рисунок). Так вы получите возможность просматривать больше закладок, чем ранее.

Математические операции Delphi

Для дальнейшего изучения языка Delphi рассмотрим математические операции. Они делятся на арифметические, логические операции и операции отношения.

Арифметические операции выполняют арифметические действия в выражениях над значениями переменных.

Таблица 5. Арифметические операции

Операция Действия Типы операндов Тип результата
+ Сложение Целый/вещественный Целый/вещественный
Вычитание Целый/вещественный Целый/вещественный
* Умножение Целый/вещественный Целый/вещественный
/ Деление Целый/вещественный Вещественный
Div Деление нацело Целый Целый
Mod Остаток от деления Целый Целый

Особое внимание следует обратить на то, что тип результата деления – всегда вещественное число.

Оператор DIV позволяет получить целую часть результата деления одного числа на другое. Например, значение выражения 5 DIV 2 равно 2.

Оператор MOD (деление по модулю) позволяет получить остаток от деления одного числа на другое. Например, значение выражения 15 MOD 7 равно 1.

При вычислении значений выражений, как и в математике, учитывается порядок действий. Сначала выполняются операторы *, /, DIV, MOD, а затем – операторы + и -. Для задания нужного порядка выполнения операций можно использовать скобки.

Операции отношениясравнивают два операнда и определяют истинность или ложность выражения. Результат операции отношения имеет тип Boolean, который принимает два значения: True (истина) и False (ложь).

Таблица 6. Операции отношения

Таблица 7. Логические операции

Операция Название Выражение Результат
= Равно А=В True, если А равно В
<> Не равно A<>B True, если А не равно В
> Больше A>B True, если А больше В
= Больше или равно A>=B True, если А больше или равно В
Операция Действие Пример выражения
Not Логическое отрицание Not A
And Логическое и A and B
Or Логическое или A or B
Xor Исключающее или A xor B

Стандартные математические функции Delphi

В языке Delphi имеются следующие математические функции:

Таблица 8. Математические функции

Функция Значение
Abs (x) Возвращает абсолютное значение (модуль) числа х
Sqrt (x) Возвращает число, равное квадратному корню из х
Sqr (x) Возвращает число, равное квадрату числа х
Sin (x) Возвращает синус числа х, где х – угол в радианах
Cos (x) Возвращает косинус числа х, где х – угол в радианах
Arctan (x) Возвращает арктангенс числа х, где х – угол в радианах
Exp (x) Возвращает число, равное е в степени х
Ln (x) Возвращает число, равное натуральному логарифму от числа х
Pi Число Пи
Random (x) Возвращает случайное число от 0 до х-1. При использовании функции без параметров генерируются случайные числа от 0 до 1.
Trunc (x) Возвращает число, равное целой части х (при округлении отбрасывается дробная часть). Усеченное число имеет тип LongInt.
Frac (x) Возвращает число, равное дробной части числа х. Например, Frac(2.4) возвращает 0.4, Frac (3.99) возвращает 0.99.
Int (x) Возвращает число, равное целой части числа х. Например, Int (2.4) возвращает 2.0, Int (0.99) возвращает 0.0.
Round (x) Возвращает число, равное целой части числа х (округление происходит по правилам математики, т.е. к ближайшему целому). Процедура является универсальной, так как результат может быть как целым, так и вещественным числом.
Ord (x) Определяет четность числа. Возвращает значение True, если число х нечетно.

Линейная алгоритмическая структура.

Последнее изменение этой страницы: 2020-01-25; Нарушение авторского права страницы

Илон Маск рекомендует:  html в HTML
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL