DEFINITION MODULE DMConversions;
(*******************************************************************
Module DMConversions ('Dialog Machine' DM_V3.0)
Copyright (c) 1986-2006 by Andreas Fischlin, Alex Itten and
ETH Zurich.
Purpose Provides routines to convert numbers into strings
and vice versa.
Remarks The routines converting a string to a number test
for syntactical correctness. Upon returning from the
routine the variable done indicates whether the
string contained a syntactically correct number or
not. The syntax for integers, cardinals, and reals
is similar to that adopted by the language
Modula-2.
IMPORTANT IMPLEMENTATION RESTRICTION:
Under the Unix Platform (BatchDM, RASS - RAMSES
Simulation Server) the implementation of this
module assumes you can compute gradual underflows
without triggering exceptions. E.g.
StringToReal("1.0E-50",...) will generate an
underflow and the resulting real number will be
denormalized. Enabling underflow exceptions will
prevent a successful completion of such string to
number conversions. Please make sure underflow
exceptions are disabled. See also module DMFloatEnv
for further details on how to set and control the
floating point environment of the computer on
which the 'Dialog Machine' is running.
This module belongs to the 'Dialog Machine'.
Programming
o Design
Andreas Fischlin 23/04/1986
o Implementation
Alex Itten 30/10/1986
Andreas Fischlin 19/03/1987
ETH Zurich
Systems Ecology
CHN E 35.1
Universitaetstrasse 16
8092 Zurich
SWITZERLAND
URLs:
<mailto:RAMSES@env.ethz.ch>
<http://www.sysecol.ethz.ch>
<http://www.sysecol.ethz.ch/SimSoftware/RAMSES>
Last revision of definition: 22/03/2000 AF
*******************************************************************)
FROM SYSTEM IMPORT BYTE;
TYPE
RealFormat = (FixedFormat, ScientificNotation);
(*
The following general conventions hold:
o Conversions from a string into a number:
The string will be analyzed and interpreted according to
the EBNF syntax as given for each converting routine. If
the conversion is not successful the parameter done will be
set to FALSE. The number variable passed to hold the
result is left untouched if the converions fails. Since
range errors (e.g. numbers of type INTEGER are restricted
to the range [-32768..+32767]) done may be FALSE although
the string content was syntactically correct. To
differentiate between a range and syntax error use
procedure IllegalSyntaxDetected immediately after calling
the conversion routine.
o Conversions from a number into a string:
The conversion of the number will produce "length" digits, if
necessary leading blanks will be inserted (exception
BytesToHexString) to produce exactly length characters. If the
number has more than "length" digits then all digits are
filled into the result string. If the result string is too
small to hold all digits, then a questionMark ("?") will be
returned in string[0].
EBNF for digits:
decDigit = ("0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9")
hexDigit = (decDigit|"A"|"a"|"B"|"b"|"C"|"c"|"D"|"d"|"E"|"e"|"F"|"f")
*)
PROCEDURE StringToCard(str: ARRAY OF CHAR; VAR card: CARDINAL;
VAR done: BOOLEAN);
(*
converts a cardinal number from str according to EBNF-syntax:
["+"]{}
The accepted range is [0..65535]
*)
PROCEDURE CardToString(card: CARDINAL; VAR str: ARRAY OF CHAR;
length: CARDINAL);
PROCEDURE StringToLongCard(str: ARRAY OF CHAR; VAR lcard: LONGCARD;
VAR done: BOOLEAN);
(*
converts a long cardinal number from str according to EBNF-syntax:
["+"]{}["D"|"d"]
The accepted range is [0D..4294967295D]
*)
PROCEDURE LongCardToString(lcard: LONGCARD; VAR str: ARRAY OF CHAR;
length: CARDINAL);
PROCEDURE StringToInt(str: ARRAY OF CHAR; VAR int: INTEGER;
VAR done: BOOLEAN);
(*
converts an integer number from str according to EBNF-syntax:
["-"|"+"]{}
The accepted range is [-32768..+32767]
*)
PROCEDURE IntToString(int: INTEGER; VAR str: ARRAY OF CHAR;
length: CARDINAL);
PROCEDURE StringToLongInt(str: ARRAY OF CHAR; VAR lint: LONGINT;
VAR done: BOOLEAN);
(*
converts a long integer number from str according to EBNF-syntax:
["-"|"+"]{}["D"|"d"]
The accepted range is [-2147483648D..+2147483647D]
*)
PROCEDURE LongIntToString(lint: LONGINT; VAR str: ARRAY OF CHAR;
length: CARDINAL);
PROCEDURE StringToReal(str:ARRAY OF CHAR; VAR real: REAL;
VAR done: BOOLEAN);
(*
converts a real number from str according to EBNF-syntax:
["-"|"+"]
(
( {} ["."|","] [{}] |
[{}] ("."|",") {} )
[ "E" ["-"|"+"] {} ]
| ( "INF" | ("NAN") "(" {} ")" )
). (syntax case insensitive)
The accepted range consists of 3 intervals:
[-3.38E+38..-3.38E-38], 0.0, [+3.38E-38..+3.38E+38]
For encoding NAN's (Not A Number) see IEEE 754 standard for
floating-point arithmetic or SANE manual, p. 41, Table 5-1.
Ex.: 0.123, 2,5, -31e+34, 2.1234E+24, .1, .1e+3, 2E-24,
NAN, -INF, +nan(017), -NaN(02)
*)
PROCEDURE RealToString(real: REAL; VAR str: ARRAY OF CHAR;
length, dec: CARDINAL; f: RealFormat);
(*
Converts a real number real into the string str, using length
digits and max. dec digits after the decimal point ".".
Depending on the chosen format f, the number is converted in
either fixed or scientific notation:
Examples:
fixed format: scientific format:
|xxxx#xxxx#xxxx# |xxxx#xxxx#xxxx# length dec comment
-123.456 -1.234E+02 10 3
-123.45 -1.23E+02 10 2
-123.45678 -1.234E+02 10 6 truncates decimal digits
123.4 1.2E+02 3 2 truncates decimal digits
and overwrites right
boundary
If str is too small to represent the number "real" correctly
(e.g. HIGH(str)<8), then a questionMark ("?") will be
returned in str[0]. Otherwise, if HIGH(str) is equal or
larger than 8 and the number is too large to be represented
in fixed format in the given string str, then the number will
be displayed in scientific format. If you want to be sure
that a real number does not take more room than a specified
length, you may use a string variable of TYPE
ARRAY[0..maxLength] OF CHAR (but maxLength must always be
greater or equal than 8).
The current implementation returns up to 19 significiant
digits, But only 6 (!) decimal digits are always correct (=
TRUNC(6.924) = TRUNC(lg10(2^23)). Type REAL reals are
single precision real numbers according to the IEEE 754
standard for floating-point arithmetic and have 23 binary
digits in their fractional part (8 for the exponent, 1 for
the sign). Positive numbers begin always with at least one
leading blank so they are as long as their negative
equivalences.
For fixed format DMConversions knows two SPECIAL CASES:
1. length = 1 Numbers are shown with significant digits always
and their representation requires as many
characters as needed (given destination string is
large enough). Thus the format may be
automatically switched accordingly. E.g.
0.0000000000001 if dec = 2 is not shown as 0.00
but as 1.00E-13. Numbers have always dec decimal
digits, unless the destination string is too
small to hold that many characters. Numbers
larger than 1.0E+7 or smaller than 1.0E-7 (7 =
number of significant digits) are shown in
ScientificNotation to minimize character usage.
If the string is to small to hold a number
according to the latter rule, the format may
change even for smaller numbers. No leading
blanks are inserted, but, positive numbers have a
single leading blank (" ") in place of the
negative sign.
2. length = 0 In adition to the case "length = 1"
- a leading blank is never inserted (positive numbers too)
- trailing zeros "0" are suppressed (in both formats).
- exponents in scientific formats have no
leading zeros
- max. 7 significiant digits are displayed, since
single precision reals (32 bit) do not support
more digits. This avoids the display of
spurious digits and prevents unwanted
ugly displays, such as 0.79999993 instead of 0.8.
This case is particularly useful when the number
is to be inserted in the middle of some text
or for interactive dialogs. Information is
always preserved up to dec significant,
decimal digits (unless dec>7) and format is chosen
according to minimum space requirements.
For interactive use it is a good idea to specify
dec not too large, i.e. up to the maximum
significant numbers, i.e. <= 7.
For output on text files use 9 decimal digits to
minimize conversion errors when subsequent
reading is planned.
Examples:
fixed format: scientific format:
|xxxx#xxxx#xxxx# |xxxx#xxxx#xxxx# length dec comment
123.50000 1.2350E+02 1 4 see special case 1
123.5 1.235E+2 0 4 see special case 2
(This feature is used in DMEntryForms to display real numbers) *)
PROCEDURE StringToLongReal(Str:ARRAY OF CHAR; VAR longReal: LONGREAL;
VAR done: BOOLEAN);
(*
converts a real number from str according to EBNF-syntax:
["-"|"+"]
(
( {} ["."|","] [{}] |
[{}] ("."|",") {} )
[ ["D"] | ("D"|"E")["-"|"+"]{} ]
| ( "INF" | ("NAN") "(" {} ")" )
). (syntax case insensitive)
The accepted range consists of 3 intervals:
[-1.79D+308..-1.79D-308], 0.0, [+1.79D-308..+1.79D+308]
For encoding NAN's (Not A Number) see IEEE 754 standard for
floating-point arithmetic or SANE manual, p. 41, Table 5-1.
Ex.: 0.123, 2,5, 0.2415D, 31e+34, 2.1234E+24, .1, .1e+3, 2D-24
NAN, -INF, +nan(017), -NaN(02)
*)
PROCEDURE LongRealToString(longreal: LONGREAL; VAR str: ARRAY OF CHAR;
length, dec: CARDINAL; f: RealFormat);
(*
Converts a real number real into the string str, using length
digits and max. dec digits after the decimal point ".".
Depending on the chosen format f, the number is converted in
either fixed or scientific notation:
Examples:
fixed format: scientific format:
|xxxx#xxxx#xxxx# |xxxx#xxxx#xxxx# length dec comment
-123.45678 -1.23478E+002 13 5
123.456 1.236E+002 11 3
-12345.678 -1.23E+004 10 5 truncates decimal digits
123.4 1.2E+002 3 > 0 truncates decimal digits
and overwrites right
boundary
If str is too small to represent the number "longreal"
correctly (e.g. HIGH(str)<9), then a questionMark ("?")
will be returned in str[0]. Otherwise, if HIGH(str) is
equal or larger than 8 and the number is too large to be
represented in fixed format in the given string str, then
the number will be displayed in scientific format. If you
want to be sure that a real number does not take more room
than a specified length, you may use a string variable of
TYPE ARRAY[0..maxLength] OF CHAR (but maxLength must always
be greater or equal than 9).
The current implementation returns up to 19 significiant
digits, But only 15 decimal digits are always correct (=
TRUNC(15.654) = TRUNC(lg10(2^52)). Type LONGREAL reals are
double precision real numbers according to the IEEE 754
standard for floating-point arithmetic and have 52 binary
digits in their fractional part (11 for the exponent, 1 for
the sign). Positive numbers begin always with at least one
leading blank so they are as long as their negative
equivalences.
For fixed format DMConversions knows two exceptional SPECIAL CASES:
1. length = 1 Numbers are shown with significant digits always
and their representation requires as many
characters as needed (given destination string is
large enough). Thus the format may be
automatically switched accordingly. E.g.
0.0000000000001 if dec = 2 is not shown as 0.00
but as 1.00E-13. Numbers have always dec decimal
digits, unless the destination string is too
small to hold that many characters. Numbers
larger than 1.0E+16 or smaller than 1.0E-16 (16 =
number of significant digits) are shown in
ScientificNotation to minimize character usage.
If the string is to small to hold a number
according to the latter rule, the format may
change even for smaller numbers. No leading
blanks are inserted, but, positive numbers have a
single leading blank (" ") in place of the
negative sign.
2. length = 0 In adition to the case "length = 1"
- a leading blank is never inserted (positive numbers too)
- trailing zeros "0" are suppressed (in both formats).
- exponents in scientific formats have no
leading zeros
- max. 16 significiant digits are displayed, since
double precision reals (64 bit) do only
support that many digits on the average
(16th digit may already be wrong in some
cases). This avoids the display of
spurious digits and prevents unwanted
ugly displays, such as 0.79999999999993
instead of 0.8.
This case is particularly useful when the number
is to be inserted in the middle of some text
or for interactive dialogs. Information is
always preserved up to dec significant,
decimal digits (unless dec>15) and format is chosen
according to minimum space requirements.
For interactive use it is a good idea to specify
dec not too large, i.e. up to the maximum
significant numbers, i.e. <= 15.
For output on text files use 17 decimal digits to
minimize conversion errors when subsequent
reading is planned.
Examples:
fixed format: scientific format:
|xxxx#xxxx#xxxx# |xxxx#xxxx#xxxx# length dec comment
123.50000 1.2350E+002 1 4 see special case 1
123.5 1.235E+2 0 4 see special case 2
*)
PROCEDURE HexStringToBytes(hstr: ARRAY OF CHAR; VAR x: ARRAY OF BYTE;
VAR done: BOOLEAN);
(*
converts the bytes from hstr according to EBNF-syntax:
{}. There must be an even number of hexDigits
present.
*)
PROCEDURE BytesToHexString(x: ARRAY OF BYTE; VAR hstr: ARRAY OF CHAR);
(*
Conversion between any variable and a hexadecimal
representation. Note that in hex 1 byte needs 2 characters.
Formatting is always done in the most simplest form, e.g. no
leading blanks.
*)
PROCEDURE SetHexDigitsUpperCase( upperC: BOOLEAN );
(* Allows to specify whether the result produced by BytesToHexString
will contain upper case letters or not *)
PROCEDURE IllegalSyntaxDetected(): BOOLEAN;
(*
Returns whether any of the procedures StringToXyz (where Xyz
stands for Card, LongCard, Int, LongInt, Real, or LongReal)
has detected a syntax error. Typically this procedure is
called if any of these conversion routines has returned done
= FALSE and you want to differenciate the cause of this
failure, i.e. whether it is due to a syntax or a range error.
*)
PROCEDURE UndefREAL(): REAL;
PROCEDURE UndefLONGREAL(): LONGREAL;
(*
These two procedures return an undefined value according to
IEEE standard for binary floating-point arithmetic IEEE Std
754-1985. The value is "NAN(017)" (NAN - Not A Number,
NANASCBIN ~ attempt to convert invalid ASCII string) and is
a signalling NAN, i.e. it will signal an exception unless
involved in a mere assignment statement. Any involvement
in numeric expressions or a relation will trigger a fatal
exception of type "invalid operand" (given of course this
exception is currently enabled).
The following codes may help you to interprete NAN numbers:
1 : invalid square root such as sqrt(-1)
2 : invalid addition such as +INF - +INF
4 : invalid division such as 0/0
8 : invalid multiply such as 0*INF
9 : invalid remainder or mod such as x REM 0
17 : attempt to convert invalid ASCII string
20 : result of converting comp NaN to floating
21 : attempt to create a NaN with a zero code such as NAN(0)
Note: NAN(0) (Macintosh) and NAN(255) (Sun) ~ INF
33 : invalid argument to trigonometric routine
34 : invalid argument to inverse trig routine
36 : invalid argument to log routine
37 : invalid argument to x^i or x^y routine
38 : invalid argument to financial routine
Note INF corresponds always to NAN(0), regardless of platform.
However, the result of operations involving operands of value
NAN and/or INF may be platform dependent.
On the Macintosh platform the following, diagnostic error
messages are produced: "Invalid operands (SANE)" or "REAL
overflow or invalid operand (System)". The actual message
displayed depends on the current floating-point arithmetic
in use. If SANE is in use ("'alwaysSANE' on" in section
"SANE" of the User.Profile), the first error message is
shown. Otherwise ("'alwaysSANE' off" in section "SANE" of
the User.Profile), then the Fink arithmetic is in use for
single precision real arithmetic and the second error
message is shown. Note, for LONGREAL, i.e. double
precision real arithmetic, MacMETH always uses SANE on the
Macintosh platform, regardless of the settings of flag
'alwaysSANE'. Note also, if SANE is in use and flag
'invalidHalt' is off, the use of UndefREAL() in an
expressions triggers never an exception.
IMPORTANT IMPLEMENTATION RESTRICTION of SANE (Macintosh
platform only): In case you set "'invalidHalt' off" in
section "SANE" of your User.Profile (or by calling
DisableHalt(invalid) from module DMFloatEnv), the
evaluation of relations involving NANs may differ from what
you might expect; see final comments below for procedure
IsUndefREAL and IsUndefLONGREAL.
Typical usage: Use these values to code that a real
variable has currently no defined value, e.g. a missing
value in a series of measurements, by assigning it to the
real variable. This behavior may be useful in various
situations to detect missing values. For instance the
curve drawing routines from module DM2DGraphs automatically
temporarily pause the plotting if such a value is encountered.
*)
PROCEDURE IsUndefREAL(x: REAL): BOOLEAN;
PROCEDURE IsUndefLONGREAL(x: LONGREAL): BOOLEAN;
(*
These two procedures allow to inspect an undefined real value in a
boolean expression. Any NAN value, i.e. Not A Number, is
detected, not only UndefREAL() values. This includes also +INF
and -INF, values which may result from overflows (By the way, note,
you may use INF values in relations such as MAX(REAL) < +INF
which evaluates to TRUE etc.).
Important note: You may call these routines regardless of the
current settings of the floating point environment (see
DMFloatEnv). This contrasts to a direct comparison such as
made in example (1)
IF x = UndefREAL() THEN ... (1)
where your program will normally be halted (assuming you
use default settings, i.e. "'invalidHalt' on" in section
"SANE" of your User.Profile). Using IsUndefREAL will allow
you to avoid the triggering of an exception as shown in
example (2)
IF IsUndefREAL(x) THEN ... (2)
Hence, whenever possible, use these routines to detect the
values UndefREAL() resp. UndefLONGREAL().
IMPORTANT IMPLEMENTATION RESTRICTION of SANE (Macintosh
platform only): In case you set "'invalidHalt' off" in
section "SANE" of your User.Profile (or by calling
DisableHalt(invalid) from module DMFloatEnv), no exception
is triggered while example (1) is executed. HOWEVER, its
evaluation will always be FALSE, even in cases where x
holds actually the value UndefREAL()!
IMPORTANT IMPLEMENTATION RESTRICTION: With compilers
supporting assignment compatibility beetween REAL and
LONGREAL such as MacMETH you can call the two routines
by using an actual parameter of different type than x.
However, in all these cases the result returned is
undefined. It is the programmer's responsibility to
use as actual real paramater only a variable of an
identical type than that of the formal parameter x.
*)
END DMConversions.