Visual studio математические выражения
Рассмотрим основные методы класса Math:
Abs(double value) : возвращает абсолютное значение для аргумента value
Acos(double value) : возвращает арккосинус value. Параметр value должен иметь значение от -1 до 1
Asin(double value) : возвращает арксинус value. Параметр value должен иметь значение от -1 до 1
Atan(double value) : возвращает арктангенс value
BigMul(int x, int y) : возвращает произведение x * y в виде объекта long
Ceiling(double value) : возвращает наименьшее целое число с плавающей точкой, которое не меньше value
Cos(double d) : возвращает косинус угла d
Cosh(double d) : возвращает гиперболический косинус угла d
DivRem(int a, int b, out int result) : возвращает результат от деления a/b, а остаток помещается в параметр result
Exp(double d) : возвращает основание натурального логарифма, возведенное в степень d
Floor(decimal d) : возвращает наибольшее целое число, которое не больше d
IEEERemainder(double a, double b) : возвращает остаток от деления a на b
Log(double d) : возвращает натуральный логарифм числа d
Log(double a, double newBase) : возвращает логарифм числа a по основанию newBase
Log10(double d) : возвращает десятичный логарифм числа d
Max(double a, double b) : возвращает максимальное число из a и b
Min(double a, double b) : возвращает минимальное число из a и b
Pow(double a, double b) : возвращает число a, возведенное в степень b
Round(double d) : возвращает число d, округленное до ближайшего целого числа
Round(double a, int b) : возвращает число a, округленное до определенного количества знаков после запятой, представленного параметром b
Sign(double value) : возвращает число 1, если число value положительное, и -1, если значение value отрицательное. Если value равно 0, то возвращает 0
Sin(double value) : возвращает синус угла value
Sinh(double value) : возвращает гиперболический синус угла value
Sqrt(double value) : возвращает квадратный корень числа value
Tan(double value) : возвращает тангенс угла value
Tanh(double value) : возвращает гиперболический тангенс угла value
Truncate(double value) : отбрасывает дробную часть числа value, возвращаяя лишь целое значние
Также класс Math определяет две константы: Math.E и Math.PI . Например, вычислим площадь круга:
Некоторые сведения относятся к предварительной версии продукта, в которую до выпуска могут быть внесены существенные изменения. Майкрософт не предоставляет никаких гарантий, явных или подразумеваемых, относительно приведенных здесь сведений.
Предоставляет константы и статические методы для тригонометрических, логарифмических и иных общих математических функций.
Примеры
В следующем примере используется несколько математических и тригонометрических функций из Math класса для вычисления внутренних углов трапециоида.
Представляет основание натурального логарифма, определяемое константой e .
Представляет отношение длины окружности к ее диаметру, определяемое константой π.
Представляет число радианов в полном обороте, заданное константой τ.
Методы
Возвращает абсолютное значение числа Decimal.
Возвращает абсолютное значение числа двойной точности с плавающей запятой.
Возвращает абсолютное значение 16-битового целого числа со знаком.
Возвращает абсолютное значение 32-битового целого числа со знаком.
Возвращает абсолютное значение 64-битового целого числа со знаком.
Возвращает абсолютное значение собственного целого числа со знаком.
Возвращает абсолютное значение числа одинарной точности с плавающей запятой.
Возвращает угол, косинус которого равен указанному числу.
Возвращает угол, гиперболический косинус которого равен указанному числу.
Возвращает угол, синус которого равен указанному числу.
Возвращает угол, гиперболический синус которого равен указанному числу.
Возвращает угол, тангенс которого равен указанному числу.
Возвращает угол, тангенс которого равен отношению двух указанных чисел.
Возвращает угол, гиперболический тангенс которого равен указанному числу.
Умножает два 32-битовых числа.
Формирует полное произведение двух 64-битовых чисел.
Формирует полное произведение двух 64-битовых чисел без знака.
Возвращает ближайшее самое маленькое значение, которое меньше, чем x .
Возвращает ближайшее самое большое значение, превышающее x .
Возвращает кубический корень из указанного числа.
Возвращает наименьшее целое число, которое больше или равно заданному десятичному числу.
Возвращает наименьшее целое число, которое больше или равно заданному числу с плавающей запятой двойной точности.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает value , ограниченное диапазоном от min до max включительно.
Возвращает значение с величиной x и знаком y .
Возвращает косинус указанного угла.
Возвращает гиперболический косинус указанного угла.
Создает цитиент и оставшуюся часть двух 16-разрядных чисел со знаком.
Создает цитент и оставшуюся часть двух 32-разрядных чисел со знаком.
Вычисляет частное двух 32-разрядных знаковых целых чисел и возвращает остаток в выходном параметре.
Создает цитиент и оставшуюся часть двух 64-разрядных чисел со знаком.
Вычисляет частное двух 64-битовых целых чисел со знаком и возвращает остаток в выходном параметре.
Создает частное и оставшееся число двух подписанных собственных чисел.
Создает цитент и оставшуюся часть двух 16-разрядных чисел без знака.
Создает цитент и оставшуюся часть двух неподписанных 32-разрядных чисел.
Создает цитент и оставшуюся часть двух 64-разрядных чисел без знака.
Создает частное и оставшееся число двух неподписанных собственных чисел.
Возвращает e , возведенное в указанную степень.
Возвращает наибольшее целое число, которое меньше или равно указанному десятичному числу.
Возвращает наибольшее целое число, которое меньше или равно заданному числу с плавающей запятой двойной точности.
Возвращает значение (x * y) + z, округленное в рамках одной тернарной операции.
Возвращает остаток от деления одного указанного числа на другое указанное число.
Возвращает целочисленный логарифм с основанием 2 указанного числа.
Возвращает натуральный логарифм (с основанием e ) указанного числа.
Возвращает логарифм указанного числа в системе счисления с указанным основанием.
Возвращает логарифм с основанием 10 указанного числа.
Возвращает логарифм с основанием 2 указанного числа.
Возвращает большее из двух десятичных чисел.
Возвращает большее из двух чисел двойной точности с плавающей запятой.
Возвращает большее из двух 16-битовых целых чисел со знаком.
Возвращает большее из двух 32-битовых целых чисел со знаком.
Возвращает большее из двух 64-битовых целых чисел со знаком.
Возвращает больше двух собственных целых чисел со знаком.
Возвращает большее из двух чисел одинарной точности с плавающей запятой.
Возвращает большее из двух 16-битовых целых чисел без знака.
Возвращает большее из двух 32-битовых целых чисел без знака.
Возвращает большее из двух 64-битовых целых чисел без знака.
Возвращает больше двух собственных целых чисел без знака.
Возвращает большую величину из двух чисел двойной точности с плавающей запятой.
Возвращает меньшее из двух десятичных чисел.
Возвращает меньшее из двух чисел двойной точности с плавающей запятой.
Возвращает меньшее из двух 16-битовых целых чисел со знаком.
Возвращает меньшее из двух 32-битовых целых чисел со знаком.
Возвращает меньшее из двух 64-битовых целых чисел со знаком.
Возвращает меньшее из двух собственных целых чисел со знаком.
Возвращает меньшее из двух чисел одинарной точности с плавающей запятой.
Возвращает меньшее из двух 16-битовых целых чисел без знака.
Возвращает меньшее из двух 32-битовых целых чисел без знака.
Возвращает меньшее из двух 64-битовых целых чисел без знака.
Возвращает меньшее из двух собственных целых чисел без знака.
Возвращает меньшую величину из двух чисел двойной точности с плавающей запятой.
Возвращает указанное число, возведенное в указанную степень.
Возвращает оценку взаимности указанного числа.
Возвращает оценку обратного квадратного корня указанного числа.
Округляет десятичное значение до ближайшего целого значения; значения посередине округляются до ближайшего четного числа.
Округляет десятичное значение до указанного числа знаков после запятой; значения посередине округляются до ближайшего четного числа.
Округляет десятичное значение до указанного числа дробных цифр, используя указанное соглашение округления.
Округляет десятичное значение целым числом, используя указанное соглашение округления.
Округляет значение с плавающей запятой двойной точности до ближайшего целого значения; значения посередине округляются до ближайшего четного числа.
Округляет значение с плавающей запятой двойной точности до указанного числа знаков после запятой; значения посередине округляются до ближайшего четного числа.
Округляет значение с плавающей запятой двойной точности до указанного числа дробных цифр, используя указанное соглашение округления.
Округляет значение с плавающей запятой двойной точности до целого числа, используя указанное соглашение округления.
Возвращает значение x * 2^n, вычисленное эффективно.
Возвращает целое число, указывающее знак десятичного числа.
Возвращает целое число, обозначающее знак числа двойной точности с плавающей запятой.
Возвращает целое число, указывающее знак 16-разрядного целого числа со знаком.
Возвращает целое число, указывающее знак 32-разрядного целого числа со знаком.
Возвращает целое число, указывающее знак 64-разрядного целого числа со знаком.
Возвращает целое число, указывающее знак целого числа со знаком собственного размера со знаком.
Возвращает целое число, обозначающее знак числа с плавающей запятой одиночной точности.
Возвращает синус указанного угла.
Возвращает синус и косинус указанного угла.
Возвращает гиперболический синус указанного угла.
Возвращает квадратный корень из указанного числа.
Возвращает тангенс указанного угла.
Возвращает гиперболический тангенс указанного угла.
Вычисляет целую часть заданного десятичного числа.
Вычисляет целую часть заданного числа двойной точности с плавающей запятой.
Используемые программы и библиотеки
Построение дерева выражения
Парсер-генератор GOLD я выбрал, поскольку уже имею опыт с ANTLR и захотелось чего-то нового. Ну а его преимущества, по сравнению с остальными, вы можете увидеть в данной таблице. Как видим, если сравнивать с ANTLR, то GOLD основан на алгоритме LALR, а не LL. А это значит, что в теории, сгенерированный парсер быстрее и мощнее, но с другой стороны, его нельзя отлаживать (в GOLD вообще подгружается файл в бинарном формате), а если бы и можно было бы, то это было бы совсем не очевидно и неудобно. Другим большим отличием является форма задания грамматики: BNF, а не EBNF. А это означает, что грамматика, записанная в данной форме, имеет немного больший размер из-за угловых скобок, знака определения и отсутствия условных вхождений и повторений (подробней в wiki. И, например, такое правило в EBNF, как
будет переписано в такой форме:
Итак, окончательная грамматика математических выражений имеет следующий вид:
В грамматике вроде бы все очевидно, за исключением лексемы: Number1 = +('.'*('('*')')?)? Данная конструкция позволяет парсить строки следующего вида: 0.1234(56), что означает рациональную дробь: 61111/495000. Про преобразование такой строки в дробь я расскажу позже.
- Calculated — узел, представляющий рассчитанную константу в приемлемом для компилятора формате double, например «0.714285714285714», «0.122222222222222». Для каждой такой константы создается новый объект, даже если они одинаковые.
- Value — узел, представляющий рассчитанную константу в формате рациональной дроби, т.е. 1 представляется как «1/1», «0.1(2)» — как «11/90», «0.1234» — как «617/5000». Для каждой такой константы создается новый объект, даже если они одинаковые.
- Constant — узел, представляющий неопределенную константу, например «a», «b» и др. Если в выражении встречаются две таких константы, то они указывают на один узел.
- Variable — узел, представляющий переменную, например «x», «y» и др. Если в выражении определенная переменная встречается несколько раз, то для нее создается только один объект, так же как и для неопределенной константы. Важно понимать, что разграничение константы и переменной важно только на этапе вывода аналитическое производной, а в других случаях это знание не обязательно.
- Function — узел, представляющий функцию, например "+", «sin», "^" и другие. Единственный узел, который может иметь потомков.
Из грамматики видно, что узлы полученного дерева либо не имеют детей (например, значения или переменные), либо имеют от одного до двух детей в случае стандартных функций (например, cos, сложение, возведение в степень). Все остальные функции могут иметь бОльшее количество аргументов, но они не рассматривались.
Таким образом, все узлы (а точнее узлы с функциями) имеют от 0 до 2 детей включительно. Это верно с теоретической точки зрения. Но на самом деле пришлось сделать так, чтобы функции "+" и "*" имели неограниченное количество детей для того, чтобы облегчить задачу симплификации, которая будет рассмотрена позже. Кстати, от таких бинарных операций, как "-" и "/" тоже пришлось отказаться по таким же причинам (они были заменены на сложение с отрицанием правой части и умножением с обращением правой части).
Итак, на этапе разбора все узлы для каждого правила попадают или извлекаются из буфера, который называется Nodes. Таким образом, в конце всего процесса в этом буфере остается одна или несколько функций с правой и левой частью. Также в процессе парсинга для того, чтобы функции сложения и умножения сразу создавались мультинарными, использовались дополнительные буферы ArgsCount и ArgsFuncTypes, хранящие текущее количество аргументов в функции и тип текущей функции соответственно. Таким образом, например для функции умножения, используется данный код:
Обработка первого множителя:
Обработка i-того множителя:
Обработка последнего множителя:
Из этого кода видно, что, например, если никакого умножения и нет, то последнюю функции умножения с нулевым количеством элементов нужно удалить с помощью PushOrRemoveFunc. Аналогичные действия нужно провести и для сложения.
Обработка функций одного аргумента, бинарных функций, констант, переменных и значений является тривиальной, и это все можно посмотреть в MathExprParser.cs.
Вычисление аналитической производной
На данном этапе нужно преобразовать функцию в ее производную.
Как известно из первого этапа, в созданном дереве выражения существуют только четыре типа узлов (пятый появляется позже). Для них и определим производную:
- Value' = 0
- Constant' = 0
- Variable' = 1
- Function' = Derivatives[Function]
Стоит отметить, что все производные не записано жестко в коде, а тоже могут вводиться и парситься динамически.
В этом списке нет сложения и умножения потому что, как упоминалось выше, это функции с несколькими аргументами. А для того, чтобы парсить такие правила, необходимо было бы еще написать много кода. По этим же причинам, здесь нет композиции функций, т.е. (f(g(x)))' = f(g(x))' * g(x)', а вместо этого все функции представлены как композиции функций.
Также, если для функции не найдена подстановка в Derivatives (т.е. неопределенная функция), то она просто заменяется на функцию со штрихом: т.е. f(x) превратится в f(x)'.
После того, как аналитическая производная успешно была получена, возникает проблема, которая заключается в том, что в получившееся выражении возникает очень много «мусора», такого как a + 0, a * 1, a * a^-1 и пр., а также получившееся выражение можно вычислить более оптимальным образом. Например, даже для простого выражения получится некрасивое выражение:
Для устранения таких таких недостатков используется упрощение.
Упрощение (симплификация) выражения
На этом этапе, в отличие от этапа вычисления производных, я не стал записывать правила симплификации в отдельном месте, для их последующего его разбора из-за того, что функции сложения и умножения являются функциями нескольких аргументов, что представляет определенную сложность в создании таких правил в контексте итеративного языка.
В начале топика я затронул тему про то, почему сложение и умножение представлено в виде n-арных функций. Представим ситуацию, изображенную на картинке ниже. Здесь видим, что a и -a сокращаются. Но как это сделать в случае бинарных функций? Для этого нужно перебирать узлы a, b и c, чтобы потом обнаружить, что a и -a являются детьми одного узла, а значит их можно сократить вместе с узлом. Но, понятное дело, что перебор деревьев это не такая простая задача, так как куда проще производить все действия в цикле сразу со всеми детьми, как это представлено на рисунке справа. Кстати, такой перебор нам позволяют сделать математические свойства ассоциативности и коммутативности.
Во время процесса симплификации возникает задача сравнения двух узлов, которые, в свою очередь, могут иметь один из четырех типов. Это нужно, например, для того, чтобы сократить такие выражения как sin(x + y) и -sin(x + y). Понятно, что можно по узлам сравнить сами узлы и все их потомки. Но проблема заключается в том, что данный метод не справится с ситуацией, когда слагаемые или множители переставлены местами, например sin(x + y) и -sin(y + x). Для разрешения данной проблемы используется предварительная сортировка слагаемых или множителей, для которых выполняется свойство коммутативности (т.е. для сложения и вычитания). Сравниваются узлы так, как показано на картинке ниже, т.е. значения меньше констант, константы меньше переменных и т.д. Для функций все немного сложнее, поскольку нужно сравнить не только их названия, но и количество аргументов и сами аргументы.
Таким образом, после всех вышеописанных преобразований и переборов, исходное выражение неплохо упрощается.
Обработка рациональных дробей
Компиляция выражения
Для преобразования полученного семантического дерева в код IL после этапа симплификации, я использовал Mono.Cecil.
В начале процесса создается сборка, класс и метод, в который будут записываться команды. Потом для каждого FuncNode рассчитывается, сколько он раз встречается в программе. Например, если есть функция sin(x^2) * cos(x^2), то в ней функция возведения x в степень 2 встречается два раза, а функции sin и cos — по одному. В дальнейшем, данная информация о повторах вычислений функций используется следующим образом (т.е. таким образом второго раза вычисления одной и той же функции не происходит):
- Ldarg — Операция загрузки определенного аргумента функции в стек.
- Ldc_R8 — Загрузка определенного значения double в стек.
- Stloc.n — Извлечение из стека последнего значения и его сохранение в локальную переменную n.
- Ldloc.n — Помещение локальной переменной n в стек.
Стоит отметить, что данные оптимизации применимы в случае отсутствия ветвления в коде, и это очевидно почему (если не совсем очевидно, то предлагаю поразмыслить). Но так как код математических функций и их производных не может содержать циклов в моем случае, то это все работает.
Быстрое возведение в степень
Я думаю, что почти все знают про алгоритм быстрого возведения числа в степень. Но ниже представлен данный алгоритм на уровне компиляции:
Стоит отметить, что алгоритм быстрого возведения в степень не является самым оптимальным алгоритмом возведения в степень. Например, ниже представлено перемножение одинаковых переменных пять раз двумя способами. Оптимизации типа x^4 + x^3 + x^2 + x = x*(x*(x*(x + 1) + 1) + 1) тоже у меня не реализованы.
- var t = a ^ 2; a*a*a*a*a*a = t ^ 2 * t — обычный «быстрый» алгоритм.
- a*a*a*a*a*a = (a ^ 3) ^ 2 — оптимальный «быстрый» алгоритм.
Кстати, еще стоит упомянуть, что для обычных фиксированных чисел double, результат перемножения для упомянутых выше перемножений в разном порядке, будет различный (т.е. (a ^ 2) ^ 2 * a ^ 2 != (a ^ 3) ^ 2). И из-за этого некоторые компиляторы не оптимизируют многие подобные выражения. По этому поводу есть интересные Q&A в stackoverlofw: Why doesn't GCC optimize a*a*a*a*a*a to (a*a*a)*(a*a*a)? и Why Math.Pow(x, 2) not optimized to x * x neither compiler nor JIT?.
Оптимизация локальных переменных
Как известно из предыдущих этапов, локальные переменные используются для хранения результата всех функций, которые встречаются в исходном выражении более 1 раза. Для работы с локальными переменными используются всего две простые инструкции: stloc и ldloc, которые используют один аргумент, отвечающий за номер данной локальной переменной. Но если номер локальной переменной инкрементировать каждый раз при появлении повторяющегося результата вычислений (для ее создания), то локальных переменных может быть очень много. Для нивелирования данной проблемы, был реализован алгоритм сжатия жизненных циклов локальных переменных, процесс которого можно наглядно увидеть на рисунке ниже. Как видим, вместо 5 локальных переменных в выражении можно использовать всего 3. Однако этот «жадный» алгоритм не является самым оптимальным перестановки, однако он вполне подходит к реализуемой задаче.
Компиляция неопределенных функций и их производных
В разработанной библиотеке можно использовать не только простые функции от одного переменного вида f(x), но и другие, такие как f(x,a,b(x)), где a — неизвестная константа, а b(x) — неизвестная функция, передаваемая в виде делегата. Как известно, определение производной от любой функции выглядит следующим образом: b(x)' = (b(x + dx) — b(x)) / dx. И когда модуль компиляции встречает неопределенную функцию, он генерирует следующий код:
Для численного вычисления производных, можно использовать и другие, более точные методы.
Тестирование
WolframAlpha
Загрузка и выгрузка сборок с помощью доменов
Создание домена, сборки из файла, создание экземпляра определенного типа и получение объектов с информацией о методах (MethodInfo):
Вычисление результата скомпилированной функции:
Выгрузка домена и удаление файла со сборкой:
Сравнение сгенерированного IL кода
Как видим, создалось большое количество локальных переменных для хранения уже рассчитанных результатов функций. Хотя в реальности локальных переменных меньше, потому что, например, для double arg_24_0 = 2.0; не создается локальная переменная, это просто константа.
Конечно, в реальных приложениях подобные выражения редко встречаются, но, тем не менее, описанные оптимизации могут использоваться в других, более прикладных случаях и для лучшего понимания работы компилятора.
Заключение
P.S. В сердечке спрятана длинная формула с учетом перестановки слагаемых, множителей и упрощения. Во время ее усложнения использовалась разработанная программа.
UPDATE: Так как никто не отгадал, что зашифровано в сердечке, то выкладываю результат. Данная формула является частным случаем многочлена Матиясевича, множество положительных значений которых при неотрицательных значениях переменных совпадает с множеством простых чисел. На википедии также есть информация.
Рассчитать значения x , определив и использовав соответствующую функцию:
Соображения
Если внимательно посмотреть на выражение, то можно заметить схожесть между каждым его слагаемым. В общем случае каждое слагаемое можно представить выражением
где a – некоторое число. В нашем случае a = 6, 13, 22.
Поэтому, целесообразно реализовать функцию, которая получает входным параметром переменную a и вычисляет данное выражение.
Выполнение
1. Запустить MS Visual Studio . Создать проект по шаблону Windows Forms Application
После загрузки MS Visual Studio нужно создать проект по шаблону Windows Forms Application . Подробный пример создания проекта по шаблону Windows Forms Application описывается в теме:
Следует напомнить, что проект создается командой
В результате откроется окно «New Project» , изображенное на рисунке 1. В этом окне задается вкладка «Visual C++» и выбирается шаблон «Windows Forms Application» . В поле Name задается имя проекта, например MyApp01 . В поле «Location:» задается папка, в которой будут размещаться рабочие файлы проекта (в нашем случае это папка D:\Programs\CPP ).
Рис. 1. Окно создания нового проекта
После выбора OK система создаст новую форму, как показано на рисунке 2.
Рис. 2. Форма приложения после создания
2. Проектирование основной формы
2.1. Размещение элементов управления на форме
В соответствии с условием задачи, в программе ничего не вводится, только проводится расчет на основе известных значений. С учетом этого, на форме размещаются следующие элементы управления:
- один элемент управления типа Label . Автоматически создается объект (переменная) класса Label с именем label1 . С помощью этого имени можно иметь доступ к результату;
- один элемент управления типа Button . Автоматически создается объект с именем button1 . Этот элемент управления будет выводить текст условия задачи;
- один элемент управления типа PictureBox (см. рис. 3). Этот элемент управления используется для визуализации сложной формулы. Формула представляется в виде файла с расширением *.jpg . В результате создается объект с именем pictureBox1 ;
- один элемент управления типа Label в нижней части формы. Этот элемент управления будет выводить результат.
После размещения элементов управления Label , Button , PictureBox форма программы будет иметь вид, как показано на рисунке 3.
Рис. 3. Форма приложения после размещения элементов управления
2.2. Настройка элементов управления Form , Label , Button
Нужно настроить следующие свойства элементов управления:
- в элементе управления label1 свойство Text = «Рассчитать значение x» ;
- в элементе управления button1 свойство Text = «Вычислить» ;
- в элементе управления label2 свойство Text = «Результат» ;
- в форме Form1 свойство Text = «Программа вычисления выражения» .
Дополнительно можно настроить другие свойства по собственному усмотрению.
2.3. Настройка элемента управления PictureBox
Поскольку элемент управления PictureBox предназначен для отображения рисунка на форме, то сначала нужно выбрать этот рисунок. Это осуществляется кликом мышкой на стрелке вправо в верхнем правом углу PictureBox, как показано на рисунке 4.
Рис. 4. Выбор меню задач PictureBox
В меню задач PictureBox выбирается команда «Choose Image…» . В результате откроется стандартное окно Windows «Open» , в котором нужно задать файл рисунка , который содержит формулу.
В нашем случае файл с формулой можно взять здесь . Этот файл был подготовлен раньше с помощью графического редактора.
После выбора файла рисунка форма приложения будет иметь вид, как показано на рисунке 5.
Рис. 5. Форма приложения после загрузки файла рисунка
- активировать (выделить) элемент управления PictureBox ;
- с помощью мышки увеличить ширину окна PictureBox ;
- свойство SizeMode установить в значение StretchImage (масштабирование) как показано на рисунке 6.
Возможен и другой вариант настройки отображения формулы с помощью свойства SizeMode .
Рис. 6. Форма приложения после настройки и свойство SizeMode
3. Добавление текста функции к программному коду
3.1. Добавление текста функции в модуль Form1.h
Функция вычисления выражения может размещаться в модуле «Form1.h» (не обязательно). Пусть эта функция называется Calc . В соответствии с условием задачи и соображений, функция должна получать один параметр целого типа. Результат работы функции есть вещественное число. Поэтому, функция будет возвращать значение типа double .
Поскольку, вся работа выполняется в одной форме, то и реализация тела функции будет внутри кода класса Form1 .
Таким образом, в нижнюю часть тела класса Form1 нужно добавить следующий программный код
В теле функции используется функция Sqrt() , которая вычисляет корень квадратный.
3.2. Текст модуля «Form1.h» главной формы программы
На данный момент текст класса Form1 в сокращенном виде следующий:
4. Программирование обработчика события клика на кнопке «Вычислить»
Последний этап разработки программы, это программирование события Click клика на кнопке «Вычислить» . Пример программирования события клика на кнопке подробно описывается в теме:
В нашем случае, обработчик события будет иметь следующий вид:
private : System::Void button1_Click(System::Object sender, System::EventArgs e) < // вычисление double x; x = Calc(6) + Calc(13) + Calc(22); // вызов функции label2->Text = "Результат p05">5. Запуск программы
Теперь можно запускать программу на выполнение. На рисунке 7 изображен результат выполнения программы
Приносим извинения. Запрошенное содержимое было удалено. Вы будете автоматически перенаправлены через 1 секунду.
Лучший отвечающий
Вопрос
Здравствуйте, подскажите как просто вычислить значение выражения.
Есть textBox и в нём записан пример в, котором могут быть основные математические операции(+-*/) и скобки.
Как вычислить значение этого выражения?
Ответы
Или, можно просто ее скомпилировать:
Вот так это работает:
- Предложено в качестве ответа LXGDARK Editor 7 мая 2015 г. 14:24
- Помечено в качестве ответа Maksim Marinov Microsoft contingent staff, Moderator 5 июня 2015 г. 6:49
Все ответы
"Просто" вряд ли. Необходимо выполнить разбор строки: выделить операции и операнды (например, с помощью регулярных выражений), затем преобразовать значения операндов из текстового представления в числовое, вычислить выражение в соответствии с приоритетом операций.
Или, можно просто ее скомпилировать:
Вот так это работает:
- Предложено в качестве ответа LXGDARK Editor 7 мая 2015 г. 14:24
- Помечено в качестве ответа Maksim Marinov Microsoft contingent staff, Moderator 5 июня 2015 г. 6:49
Или, можно просто ее скомпилировать:
Алексей так и не понял зачем вы в начале создали делегат Calc?
Перевел код на VB и попутно разбирался как он работает. Выяснилось, что работает без этой строчки. Вот и стало интересно. Плюс я так до конца и не понял сути делегатов, зачем они и куда )))
UPD. А на кой там вообще делегаты? Переделал код без них и все работает. Объясните неучу, чего я лишился убрав их?
У меня метод его возвращает. В принципе в данном случае можно и без него. Но если, например, необходимо передать параметры, то лучше сделать лямбду с параметром. Ну и сохранив ее в делегате, можно вызывать несколько раз передавая разные параметры.
1. ". сохранив ее ( лямбду ) в делегате, можно вызывать несколько раз передавая разные параметры. "
Это как? Желательно код!
2. Какие операции поддерживаются?
+,-,*,/ - это понятно, а возведение в степень, извлечение корня, логарифмы, и пр.
3. public delegate double Calc (); - эта строка в первом случае действительно не нужна,
а в MyNamespace без этой строчки программа не работает
Блин смотрю у вас тут целая дискуссия по созданию проги для вычисления любого выражения. Согласна, решение прикольное, но у него есть минусы: скорость - так как приходится задействовать жесткий диск, антивирус - он может придираться к вновь созданным программам, компилятор - только он проверяет корректность вычисляемого выражения и еще множество подводных камней лежит на данном пути, который с первого взгляда кажется маной небесной. Парсер выражений программный не простая штука, но много лучше и в конечном итоге оправдывает себя.
1. ". сохранив ее ( лямбду ) в делегате, можно вызывать несколько раз передавая разные параметры. "
Это как? Желательно код!
Конечно, как человек преподававшие теорию компиляторов, я с вами полностью согласен. Но как известно, каков вопрос, таков ответ. В данном случае это решение простое и рабочее. В реальном проекте я бы так не делал.
Все завит от конкретной титуации. Я использовал решение Алексея впроге, которая установлена на 4х компьютерах. В данной ситуации минимальные трудозатраты оправдывают вероятные проблемы
Центры разработки
Обучение
Сообщество
Свяжитесь с нами
Программы
© 2022 Microsoft
Читайте также: