XExpression Module
An algebraic-text-string-to-numeric conversion tool
by Mike Gaitens
Version 1.0
15-Sep-2006
Xeffort's XNumber
does basic text-to-numeric conversion, and as such is the complement of the XString
function. The given text
string may contain a single decimal point.
Numbers
can be coded in scientific notation provided the exponent is a simple integer
and is preceded by an explicit E, e, D, or d (these four letters are treated as
equivalent). Spaces may be used freely. An all-blank field yields 0. However XNumber is restricted to text
patterns that make sense to Fortran as a decimal value, i.e., approximately
correspond to an I, F, E, or G format. XNumber
can return an integer, or a single- or double-precision value, and is intended
to be a general-purpose routine.
XNumberEx is intended for numeric input
conversion in engineering and/or scientific applications. It presumes the program user will be
comfortable with, and thinks in terms of, algebraic expressions. XNumberEx supports various levels of
complexity chosen by the programmer; the choice depends primarily on the needs
of the users and the practicality of teaching them the simple required syntax. The three levels of complexity are:
1.
Numeric
expressions including elementary functions.
Allows entry of complex numbers.
Eliminates need for a calculator to prepare data for the program.
2.
Programmer
defines Parameters that can be used for units conversion. User’s inputs tagged with a Parameter are
automatically converted to the required units.
3.
User
defines Parameters that can be used for any purpose – allowing the user to
parameterize all input data thus greatly ease input-sensitivity studies.
The
following sections discuss each level in more detail.
A given
text string can contain a numeric expression consisting of two or more of
XNumber-type numeric strings joined by operator symbols. The operator symbols are the usual:
|
+ |
denotes addition or unary plus (identity) |
|
- |
denotes subtraction or unary minus (negation) |
|
* |
denotes multiplication |
|
/ |
denotes division |
|
** |
denotes exponentiation |
|
^ |
denotes exponentiation |
Within each term the
normal rules of precedence apply, i.e., unary plus or minus, then
exponentiation, then multiplication and division in left-to-right order, and
finally addition and subtraction.
Matched sets of parentheses can be used to group terms. Braces and brackets can be used as
parentheses. When we say parentheses
we mean ( ) or [ ] or { }.
Some valid, if absurd,
examples follow:
|
4e-3*2000.00 |
is the same as 8 |
|
4*[2+3] |
is the same as 20 |
|
4[2+3] |
is the same as 20 (note implied multiplication) |
|
/2 |
is the same as 0.5 (note implied
numerator is 1) |
|
36^(1/2)/-3 |
is the same as -2 (note a leading minus sign would yield an
imaginary result as the program takes the square root of -36; but
-{36^[1/2]/3} is ok as a real –2 ). |
Two forms are available to
enter complex numbers – (real, imaginary) form and (magnitude <
phase-angle-in-degrees) form. Note
the encompassing parentheses (or braces or brackets) are always required. The two values are separated by a comma for
the first form, and by a < (which is intended to be reminiscent of an Đ angle symbol) for the second form. For example, considering the geometry of the
3,4,5 triangle,
[5 <36.87] is a
close approximation to the complex number
{4, 3}
Some basic functions are
also available. Note each function name
starts with a number (#) character.
Name letters are case-blind, i.e., upper, lower, or mixed case are
equivalent. Function names are always
followed by a parentheses pair which enclose the function argument(s). Multiple arguments are separated by a comma.
|
Function Name |
No. of Arguments |
Argument Type |
Description |
|
#abs |
1 |
any |
Absolute value (or magnitude of complex) |
|
#atan2 |
2 |
real |
Arctangent in radians of (2nd/1st) |
|
#atan2d |
2 |
real |
Arctangent in degrees of (2nd/1st) |
|
#cmplx |
2 |
real |
Form complex; same as (1st, 2nd) |
|
#conjg |
1 |
complex |
Conjugate of complex argument |
|
#cos |
1 |
real |
Cosine of given angle in radians |
|
#cosd |
1 |
real |
Cosine of given angle in degrees |
|
#exp |
1 |
any |
e^argument where e is base of natural logs |
|
#if |
3 |
real, any |
if the 1st argument, which must be real, is.GE. 0, then return the 2nd
argument; else if 1st is .LT. 0 return 3rd |
|
#imag |
1 |
complex |
Returns the imaginary part of the argument |
|
#int |
1 |
any |
Integer by truncating fractional real part |
|
#ln |
1 |
any |
Natural logarithm [real (arg)
> 0] |
|
#log |
1 |
real |
Logarithm base 10 [argument
> 0] |
|
#max |
2 to 4 |
real |
Maximum value of the arguments |
|
#min |
2 to 4 |
real |
Minimum value of the arguments |
|
#mod |
2 |
real |
Remainder of (1st/2nd) [2nd
not 0] |
|
#nint |
1 |
any |
Nearest integer to real part |
|
#real |
1 |
any |
Returns the real part of the argument |
|
#sin |
1 |
real |
Sine of given angle in radians |
|
#sind |
1 |
real |
Sine of given angle in degrees |
|
#sqrt |
1 |
any |
Square root of the argument |
Again, some valid, if
absurd, examples follow:
4*#SIND(30.) is
the same as 2
#Abs[(-3,4)] is
the same as 5 (magnitude of complex
number)
Note the double set of parentheses – the
inner set is required to denote a complex number while the outer set is
required to enclose the function argument.
4 + 3#Sqrt (-1) is the complex number (4,
3)
( 5 < #atan2d (3,4) ) is also the
complex number (4, 3)
Many of the above
functions make little sense in the context discussed so far; they exist to
support Parameters.
As in
Fortran, parameters are named constants that may represent an integer, real, or
complex value.
Parameter
names are composed of 1 to 16 letters, digits, and/or the underscore. The first character must be a letter. The names are always treated as case-blind
so letter case is immaterial.
Note that the letters E
and D are reserved for decimal exponents thus cannot be names of
parameters. Moreover, any parameter
name starting with an E or D must also have a letter as the second character in
that name.
Parameters can be defined
in terms of other parameters that have been previously defined.
The
primary use of such parameters is for units conversion. For instance, if you have an architectural
application that requires an input dimension in inches, and you have defined
the parameter “ft” to be 12, users of your application will be able to provide
that dimension by giving text such as
5ft + 3 + 7/16 –( 2 ft + 4 + 5/8)
which
will be decoded as 34.8125
Now let
us consider a more comprehensive example.
Suppose your application requires inputs that include dimensions in
inches, force in lb, stiffness in lb/inch, and torque in inch-lb. However, you know your users may have been
given such data in metric units.
Therefore, you might define the following parameters:
|
mm =
1/25.4 |
!
what to multiply millimeters by to get inches |
|
cm =
10 mm |
!
what to multiply cm by to get inches |
|
m=1000mm |
!
what to multiply meters by to get inches |
|
N=0.224808943 |
!
what to multiply newtons by to get lb |
|
kN=
1000 * n |
!
what to multiply kilo-newtons by to get lb |
At this
point your program is set to accept and automatically convert metric
dimensions, forces, and stiffnesses as long as the user flags the input using
the above parameters to indicate the metric unit. e.g.,
|
437
mm |
!
decoded as |
17.2…
inches |
|
1.54
* m |
! |
60.6…
inches |
|
6kN |
! |
1348.8…
lb |
|
17
N/mm |
! |
97.0…
lb/inch |
However,
due to the various ways the products of units are commonly written, there are
potential problems in coding torques.
For example,
|
3*N*m |
!
always acceptable |
|
3N m |
!
acceptable, space denotes multiplication |
|
3 Nm |
!
Trapped error. No parameter named NM |
|
3 N-m |
!
SNEAKY BUG. Says to form (3*N) and
subtract M (1000/25.4) from it.
Definitely not the user’s intention. |
The
workaround for the “no parameter” problem demonstrated on the third line above
is to merely define additional parameters representing products the user is
likely to use, e.g.,
Nm = N*m
kNm = kn*m
Nmm = N*mm
etc.
To
avoid the SNEAKY BUG problem (unintended subtraction), each parameter has an
associated logical that when set true prevents the parameter from being added
or subtracted; in essence, an adjacent + or – sign is converted to a *, so the
parameter is always a multiplicand.
This flag should be set true for every parameter defined in this section
to indicate the parameter is intended for use as a conversion factor only.
If the
programmer allows, the user could parameterize his problem by defining
parameters such as
|
Pi =
#atan2(-1, 0) |
!
exact Pi |
|
dia=42.5 |
!
diameter of surge tank |
|
ht=
100 |
!
fluid height |
|
R=dia/2 |
!
tank radius |
|
VOL=pi
* R**2 * ht |
!
fluid volume |
|
g=386 |
!
accel of gravity (in/sec^2) |
|
density=58.5
/ 1728 |
!
fluid density (lb/inch^3) |
Then
when asked to provide the mass of the fluid, the user might enter
g * vol * density
The
advantages to the user of this approach are:
·
Input
is expressed in meaningful terms that user is comfortable with.
·
No
calculator is required to prepare data for the program.
·
Input
changes can be localized to one place (the parameter list), therefore “what if”
studies can be readily accomplished.
Note
the parameter definitions form a list.
In order to prevent circular references, there is a restriction that Parameters
be Defined Before they are Used.
That is, any parameter used in a definition (i.e., on the right-side)
must have been defined earlier in the list.
Spaces
may be freely placed in an expression to improve clarity. However, a space is never allowed within a
parameter or function name. Hopefully,
handling of space characters matches the user’s expectations, namely,
·
spaces
adjacent to an operator symbol are ignored, e.g., A + B same as A+B
·
spaces
between a function name and the parenthesis starting its argument list are
ignored. e.g., #SinD (60)
·
spaces
between digits are ignored (hence spaces may be used as a thousands separator). e.g., 1 234 567 equals 1.234567 E+6
·
otherwise,
in the absence of an explicit operator symbol, one or more spaces denotes
multiplication, e.g., A B same as A*B but
3 Q 2 2 would be 66*Q
·
exponent
fields end at the first space which follows an exponent digit. e.g. 0.12E2 2 is
24, not 12E20
Download
this small demo program. Type the
expression to be evaluated in the “Test Expression” edit field and press the
“Evaluate” button. The “Parameters”
button launches a tabbed dialog – use the 1st tab to enter or edit
parameters, use the 2nd tab to display all parameters, and use the 3rd
tab to re-arrange parameters if necessary to obey the Parameters be Defined
Before they are Used ordering restriction.
XNumber
and XNumberEx are used in exactly the same way. Just substitute the name XNumberEx for the name XNumber and USE
the appropriate module. And perhaps
widen the edit fields in the dialog since expressions may take more room than
simple numbers. Currently, expressions
are limited to 64 characters. An
outline of typical coding follows:
USE Xeffort
USE Xexpression
USE DFWIN, ONLY : IDOK
! typically
these are placed in common or in a module
CHARACTER(64) :: FLMASS_TEXT = “ “
REAL FLMASS
INCLUDE ‘RESOURCE.FD.’
TYPE (x_WINDOW) :: xDlg
LOGICAL OK
OK = XLoadDialog (IDD_DIALOG, xDlg)
OK = XCtlSet (xDlg, ID, FLMASS_TEXT) ! show previous user input
500 IF
(XModalDialog (xDlg) .EQ. IDOK) THEN
!
user pressed OK button, retrieve contents
OK =
XCtlGet (xDlg, ID, FLMASS_TEXT)
!
convert to real; if conversion fails, re-open
!
dialog so user can correct the error
IF (.NOT.
XNumberEx (FLMASS_TEXT, FLMASS) ) GO TO 500
!
now FLMASS is ready to use in calculations
ENDIF
CALL XDestroyDialog (xDlg)
END
Notes
on the above:
1.
Module Xexpression contains XNumberEx; XNumber is in Xeffort.
2.
Both
XNumber and XNumberEx are logical functions that return .FALSE. if the
text-to-numeric conversion fails.
However, XNumberEx will display a custom message box explaining the
problem before returning .FALSE.
3.
Storing
the text of each dialog input field so when the dialog is re-displayed the user
can edit the input expression is a very good idea.
If you
wish to add programmer-defined parameters, simply call the logical function XParamNew
for each, e.g.,
OK = XParamNew (name, expr, note, .TRUE.)
where
name is the parameter name (16 chars. max.) , expr is what it equals as text
(64 chars. max.), the note field (64 chars. max.) is for documentation, and the
fourth argument is true to restrict the parameter for use as a factor
only. These definitions need be done
only once, and before displaying a dialog where the user might employ them.
If you
wish to enable user-defined parameters, from the download sample project extract
the ParamDlg.f90 file and add it to your project. Also copy to your resource file the 4 dialogs and 2 icons (IDLG_PARAMDEF, IDLG_PARAMEDIT, IDLG_PARAMLIST,
IDLG_PARAMMOVE, PARAM_ICON1, PARAM_ICON2) that ParamDlg.f90 uses.
It is assumed you will add a menu pick (likely named Parameters) to your
program. When this menu item is
selected, simply CALL
PARAM_ENTRY to allow
the user to enter and maintain his/her parameters.
Suppose,
as is typical in Windows programs, you have many input dialogs, including the
above Parameter Definition dialog, that the user can visit in random order,
then you run a solution upon demand.
Consider calling XNumberEx twice for each expression input field – once
when the dialog is closed to catch/warn/correct any input errors; and again
just before running the solution to re-evaluate all inputs to insure the most
recent parameter values are being used, even if the dialog that defined the
input expression has not been re-opened.
Home
Top Download Demo Executable
Download Demo Project
E-mail me