for review: float/math changes (round 2)
Andrew Bromage
bromage at cs.mu.OZ.AU
Tue Sep 8 11:45:12 AEST 1998
G'day all.
Here's round 2. Fergus' fixes have been incorporated, plus one or two
minor documentation fixes.
Cheers,
Andrew Bromage
Estimated hours taken: 3
library/math.m:
library/float.m:
Addition of four new system constants (float__radix,
float__mantissa_digits, float__min_exponent and
float__max_exponent) plus predicate equivalents. Also
added in some extra documentation for the other constants.
Rename floating point constants using the C coding standard
way (ML_*).
Put code for mathematical domain checking inside
`#ifndef ML_OMIT_MATH_DOMAIN_CHECKS', so that the user
can disable domain checking. (Note: This is actually safe,
since the combination of floating point hardware and -lm
should do all these checks for you.)
NEWS:
Mention the above changes.
--- NEWS Tue Sep 8 11:40:08 1998
+++ NEWS.float-changes Tue Sep 8 11:40:59 1998
@@ -328,6 +328,10 @@
relation__from_assoc_list/2, relation__compose/3, varset__select/3
and eqvclass__same_eqvclass_list/2.
+ In addition, there are four new system constants added to the float
+ library module, float__radix, float__mantissa_digits, float__min_exponent
+ and float__max_exponent. There are also predicate equivalents for these.
+
Also the old relation__to_assoc_list/2 predicate has been renamed as
relation__to_key_assoc_list/2; there is a new relation__to_assoc_list/2
predicate with a different type for the second argument.
@@ -380,6 +384,15 @@
define the C macro ML_OMIT_ARRAY_BOUNDS_CHECKS (e.g. by using
`MGNUCFLAGS=-DML_OMIT_ARRAY_BOUNDS_CHECKS' in your Mmake file).
[XXX we also need to fix problems with intermodule inlining heuristics.]
+
+* Domain checking for higher mathematical operations can now be disabled.
+
+ To disable array bounds checking, you must compile with
+ `--intermodule-optimization' enabled and you must also
+ define the C macro ML_OMIT_MATH_DOMAIN_CHECKS (e.g. by using
+ `MGNUCFLAGS=-DML_OMIT_MATH_DOMAIN_CHECKS' in your Mmake file).
+
+ See the Mercury Library Reference Manual for details.
* We've added some primitive debugging support.
Index: library/float.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/library/float.m,v
retrieving revision 1.27
diff -u -t -u -r1.27 float.m
--- float.m 1998/07/26 14:41:58 1.27
+++ float.m 1998/09/08 01:14:27
@@ -151,14 +151,48 @@
%
% Maximum floating-point number
+ %
+ % max = (1 - radix ** mantissa_digits) * radix ** max_exponent
+ %
:- func float__max = float.
% Minimum normalised floating-point number
+ %
+ % min = radix ** (min_exponent - 1)
+ %
:- func float__min = float.
% Smallest number x such that 1.0 + x \= 1.0
+ % This represents the largest relative spacing of two
+ % consecutive floating point numbers.
+ %
+ % epsilon = radix ** (1 - mantissa_digits)
:- func float__epsilon = float.
+ % Radix of the floating-point representation.
+ % In the literature, this is sometimes referred to as `b'.
+ %
+:- func float__radix = int.
+
+ % The number of base-radix digits in the mantissa. In the
+ % literature, this is sometimes referred to as `p' or `t'.
+ %
+:- func float__mantissa_digits = int.
+
+ % Minimum negative integer such that:
+ % radix ** (min_exponent - 1)
+ % is a normalised floating-point number. In the literature,
+ % this is sometimes referred to as `e_min'.
+ %
+:- func float__min_exponent = int.
+
+ % Maximum integer such that:
+ % radix ** (max_exponent - 1)
+ % is a normalised floating-point number. In the literature,
+ % this is sometimes referred to as `e_max'.
+ %
+:- func float__max_exponent = int.
+
%---------------------------------------------------------------------------%
% Predicate versions of the functions declared above.
@@ -237,6 +271,22 @@
:- pred float__epsilon(float).
:- mode float__epsilon(out) is det.
+ % Radix of the floating-point representation.
+:- pred float__radix(int).
+:- mode float__radix(out) is det.
+
+ % The number of base-radix digits in the mantissa.
+:- pred float__mantissa_digits(int).
+:- mode float__mantissa_digits(out) is det.
+
+ % Smallest exponent of a normalised floating-point number.
+:- pred float__min_exponent(int).
+:- mode float__min_exponent(out) is det.
+
+ % Largest exponent of a normalised floating-point number.
+:- pred float__max_exponent(int).
+:- mode float__max_exponent(out) is det.
+
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
@@ -316,9 +366,8 @@
% float__ceiling_to_int(X, Ceil) is true if Ceil is the
% smallest integer not less than X.
-:- pragma c_code(
- float__ceiling_to_int(X :: in, Ceil :: out),
- will_not_call_mercury,
+:- pragma c_code(float__ceiling_to_int(X :: in, Ceil :: out),
+ [will_not_call_mercury, thread_safe],
"
Ceil = (Integer) ceil(X);
").
@@ -327,9 +376,8 @@
% float__floor_to_int(X, Floor) is true if Floor is the
% largest integer not greater than X.
-:- pragma c_code(
- float__floor_to_int(X :: in, Floor :: out),
- will_not_call_mercury,
+:- pragma c_code(float__floor_to_int(X :: in, Floor :: out),
+ [will_not_call_mercury, thread_safe],
"
Floor = (Integer) floor(X);
").
@@ -339,9 +387,8 @@
% float__round_to_int(X, Round) is true if Round is the
% integer closest to X. If X has a fractional value of
% 0.5, it is rounded up.
-:- pragma c_code(
- float__round_to_int(X :: in, Round :: out),
- will_not_call_mercury,
+:- pragma c_code(float__round_to_int(X :: in, Round :: out),
+ [will_not_call_mercury, thread_safe],
"
Round = (Integer) floor(X + 0.5);
").
@@ -350,9 +397,8 @@
% float__truncate_to_int(X, Trunc) is true if Trunc is
% the integer closest to X such that |Trunc| =< |X|.
-:- pragma c_code(
- float__truncate_to_int(X :: in, Trunc :: out),
- will_not_call_mercury,
+:- pragma c_code(float__truncate_to_int(X :: in, Trunc :: out),
+ [will_not_call_mercury, thread_safe],
"
Trunc = (Integer) X;
").
@@ -415,9 +461,8 @@
float__pow(X, Exp) = Pow :- float__pow(X, Exp, Pow).
-:- pragma c_code(
- float__hash(F::in, H::out),
- will_not_call_mercury,
+:- pragma c_code(float__hash(F::in, H::out),
+ [will_not_call_mercury, thread_safe],
"
H = hash_float(F);
").
@@ -433,35 +478,78 @@
:- pragma c_header_code("
+ #define ML_FLOAT_RADIX FLT_RADIX /* There is no DBL_RADIX. */
+
#if defined USE_SINGLE_PREC_FLOAT
- #define MERCURY_FLOAT_MAX FLT_MAX
- #define MERCURY_FLOAT_MIN FLT_MIN
- #define MERCURY_FLOAT_EPSILON FLT_EPSILON
+ #define ML_FLOAT_MAX FLT_MAX
+ #define ML_FLOAT_MIN FLT_MIN
+ #define ML_FLOAT_EPSILON FLT_EPSILON
+ #define ML_FLOAT_MANT_DIG FLT_MANT_DIG
+ #define ML_FLOAT_MIN_EXP FLT_MIN_EXP
+ #define ML_FLOAT_MAX_EXP FLT_MAX_EXP
#else
- #define MERCURY_FLOAT_MAX DBL_MAX
- #define MERCURY_FLOAT_MIN DBL_MIN
- #define MERCURY_FLOAT_EPSILON DBL_EPSILON
+ #define ML_FLOAT_MAX DBL_MAX
+ #define ML_FLOAT_MIN DBL_MIN
+ #define ML_FLOAT_EPSILON DBL_EPSILON
+ #define ML_FLOAT_MANT_DIG DBL_MANT_DIG
+ #define ML_FLOAT_MIN_EXP DBL_MIN_EXP
+ #define ML_FLOAT_MAX_EXP DBL_MAX_EXP
#endif
").
% Maximum floating-point number
-:- pragma c_code(float__max(Max::out), will_not_call_mercury,
- "Max = MERCURY_FLOAT_MAX;").
+:- pragma c_code(float__max(Max::out),
+ [will_not_call_mercury, thread_safe],
+ "Max = ML_FLOAT_MAX;").
float__max = Max :- float__max(Max).
% Minimum normalised floating-point number */
-:- pragma c_code(float__min(Min::out), will_not_call_mercury,
- "Min = MERCURY_FLOAT_MIN;").
+:- pragma c_code(float__min(Min::out),
+ [will_not_call_mercury, thread_safe],
+ "Min = ML_FLOAT_MIN;").
float__min = Min :- float__min(Min).
% Smallest x such that x \= 1.0 + x
-:- pragma c_code(float__epsilon(Eps::out), will_not_call_mercury,
- "Eps = MERCURY_FLOAT_EPSILON;").
+:- pragma c_code(float__epsilon(Eps::out),
+ [will_not_call_mercury, thread_safe],
+ "Eps = ML_FLOAT_EPSILON;").
float__epsilon = Epsilon :- float__epsilon(Epsilon).
+
+ % Radix of the floating-point representation.
+:- pragma c_code(float__radix(Radix::out),
+ [will_not_call_mercury, thread_safe],
+ "Radix = ML_FLOAT_RADIX;").
+
+float__radix = Radix :- float__radix(Radix).
+
+ % The number of base-radix digits in the mantissa.
+:- pragma c_code(float__mantissa_digits(MantDig::out),
+ [will_not_call_mercury, thread_safe],
+ "MantDig = ML_FLOAT_MANT_DIG;").
+
+float__mantissa_digits = MantissaDig :- float__mantissa_digits(MantissaDig).
+
+ % Minimum negative integer such that:
+ % radix ** (min_exponent - 1)
+ % is a normalised floating-point number.
+:- pragma c_code(float__min_exponent(MinExp::out),
+ [will_not_call_mercury, thread_safe],
+ "MinExp = ML_FLOAT_MIN_EXP;").
+
+float__min_exponent = MinExponent :- float__min_exponent(MinExponent).
+
+ % Maximum integer such that:
+ % radix ** (max_exponent - 1)
+ % is a normalised floating-point number.
+:- pragma c_code(float__max_exponent(MaxExp::out),
+ [will_not_call_mercury, thread_safe],
+ "MaxExp = ML_FLOAT_MIN_EXP;").
+
+float__max_exponent = MaxExponent :- float__max_exponent(MaxExponent).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
Index: library/math.m
===================================================================
RCS file: /home/staff/zs/imp/mercury/library/math.m,v
retrieving revision 1.15
diff -u -t -u -r1.15 math.m
--- math.m 1998/08/24 04:45:24 1.15
+++ math.m 1998/09/04 07:51:13
@@ -11,11 +11,34 @@
% Higher mathematical operations. (The basics are in float.m.)
% The predicates in this module are not yet implemented in Prolog.
%
-% Domain errors are currently handled by a program abort. This is
-% because Mercury currently does not have exceptions built in.
+% By default, domain errors are currently handled by a program abort.
+% This is because Mercury currently does not have exceptions built in.
% Exception-handling would be nice, but it's kind of low on the
% priority scale.
%
+% For better performance, it is possible to disable the Mercury domain
+% checking by compiling with `--intermodule-optimization' and the C macro
+% symbol `ML_OMIT_MATH_DOMAIN_CHECKS' defined, e.g. by using
+% `MCFLAGS=--intermodule-optimization' and
+% `MGNUCFLAGS=-DML_OMIT_MATH_DOMAIN_CHECKS' in your Mmakefile,
+% or by compiling with the command
+% `mmc --intermodule-optimization --cflags -DML_OMIT_MATH_DOMAIN_CHECKS'.
+%
+% For maximum performance, all Mercury domain checking can be disabled by
+% recompiling this module using `MGNUCFLAGS=-DML_OMIT_MATH_DOMAIN_CHECKS'
+% or `mmc --cflags -DML_OMIT_MATH_DOMAIN_CHECKS' as above. You can
+% either recompile the entire library, or just copy `math.m' to your
+% application's source directory and link with it directly instead of as
+% part of the library.
+%
+% Note that the above performance improvements are semantically safe,
+% since the C math library and/or floating point hardware perform these
+% checks for you. The benefit of having the Mercury library perform the
+% checks instead is that Mercury will tell you in which function or
+% predicate the error occurred, as well as giving you a stack trace if
+% that is enabled; with the checks disabled you only have the information
+% that the floating-point exception signal handler gives you.
+%
%---------------------------------------------------------------------------%
:- module math.
@@ -178,11 +201,11 @@
** Mathematical constants.
*/
- #define MERCURY_FLOAT__E 2.7182818284590452354
- #define MERCURY_FLOAT__PI 3.1415926535897932384
- #define MERCURY_FLOAT__LN2 0.69314718055994530941
+ #define ML_FLOAT_E 2.7182818284590452354
+ #define ML_FLOAT_PI 3.1415926535897932384
+ #define ML_FLOAT_LN2 0.69314718055994530941
- void mercury_domain_error(const char *where);
+ void ML_math_domain_error(const char *where);
"). % end pragma c_header_code
@@ -194,12 +217,14 @@
** Handle domain errors.
*/
void
- mercury_domain_error(const char *where)
+ ML_math_domain_error(const char *where)
{
fflush(stdout);
fprintf(stderr,
""Software error: Domain error in call to `%s'\n"",
where);
+ MR_trace_report(stderr);
+ MR_dump_stack(MR_succip, MR_sp, MR_curfr);
exit(1);
}
@@ -210,12 +235,12 @@
%
% Pythagoras' number
:- pragma c_code(math__pi = (Pi::out), [will_not_call_mercury, thread_safe],"
- Pi = MERCURY_FLOAT__PI;
+ Pi = ML_FLOAT_PI;
").
% Base of natural logarithms
:- pragma c_code(math__e = (E::out), [will_not_call_mercury, thread_safe],"
- E = MERCURY_FLOAT__E;
+ E = ML_FLOAT_E;
").
%
@@ -253,9 +278,9 @@
:- pragma c_code(math__truncate(X::in) = (Trunc::out),
[will_not_call_mercury, thread_safe],"
if (X < 0.0) {
- Trunc = ceil(X);
+ Trunc = ceil(X);
} else {
- Trunc = floor(X);
+ Trunc = floor(X);
}
").
@@ -267,10 +292,12 @@
% X >= 0
%
:- pragma c_code(math__sqrt(X::in) = (SquareRoot::out),
- [will_not_call_mercury, thread_safe],"
+ [will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X < 0.0) {
- mercury_domain_error(""math__sqrt"");
+ ML_math_domain_error(""math__sqrt"");
}
+#endif
SquareRoot = sqrt(X);
").
@@ -283,18 +310,22 @@
% X = 0 implies Y > 0
%
:- pragma c_code(math__pow(X::in, Y::in) = (Res::out),
- [will_not_call_mercury, thread_safe],"
+ [will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X < 0.0) {
- mercury_domain_error(""math__pow"");
+ ML_math_domain_error(""math__pow"");
}
if (X == 0.0) {
- if (Y <= 0.0) {
- mercury_domain_error(""math__pow"");
- }
- Res = 0.0;
+ if (Y <= 0.0) {
+ ML_math_domain_error(""math__pow"");
+ }
+ Res = 0.0;
} else {
- Res = pow(X, Y);
+ Res = pow(X, Y);
}
+#else
+ Res = pow(X, Y);
+#endif
").
%
@@ -314,10 +345,12 @@
% X > 0
%
:- pragma c_code(math__ln(X::in) = (Log::out),
- [will_not_call_mercury, thread_safe],"
+ [will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X <= 0.0) {
- mercury_domain_error(""math__ln"");
+ ML_math_domain_error(""math__ln"");
}
+#endif
Log = log(X);
").
@@ -329,9 +362,12 @@
% X > 0
%
:- pragma c_code(math__log10(X::in) = (Log10::out),
- [will_not_call_mercury, thread_safe],"
- if (X <= 0.0)
- mercury_domain_error(""math__log10"");
+ [will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
+ if (X <= 0.0) {
+ ML_math_domain_error(""math__log10"");
+ }
+#endif
Log10 = log10(X);
").
@@ -343,11 +379,13 @@
% X > 0
%
:- pragma c_code(math__log2(X::in) = (Log2::out),
- [will_not_call_mercury, thread_safe],"
+ [will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X <= 0.0) {
- mercury_domain_error(""math__log2"");
+ ML_math_domain_error(""math__log2"");
}
- Log2 = log(X) / MERCURY_FLOAT__LN2;
+#endif
+ Log2 = log(X) / ML_FLOAT_LN2;
").
%
@@ -360,13 +398,15 @@
% B \= 1
%
:- pragma c_code(math__log(B::in, X::in) = (Log::out),
- [will_not_call_mercury, thread_safe],"
+ [will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X <= 0.0 || B <= 0.0) {
- mercury_domain_error(""math__log"");
+ ML_math_domain_error(""math__log"");
}
if (B == 1.0) {
- mercury_domain_error(""math__log"");
+ ML_math_domain_error(""math__log"");
}
+#endif
Log = log(X)/log(B);
").
@@ -402,10 +442,12 @@
% X must be in the range [-1,1]
%
:- pragma c_code(math__asin(X::in) = (ASin::out),
- [will_not_call_mercury, thread_safe],"
+ [will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X < -1.0 || X > 1.0) {
- mercury_domain_error(""math__asin"");
+ ML_math_domain_error(""math__asin"");
}
+#endif
ASin = asin(X);
").
@@ -417,10 +459,12 @@
% X must be in the range [-1,1]
%
:- pragma c_code(math__acos(X::in) = (ACos::out),
- [will_not_call_mercury, thread_safe],"
+ [will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X < -1.0 || X > 1.0) {
- mercury_domain_error(""math__acos"");
+ ML_math_domain_error(""math__acos"");
}
+#endif
ACos = acos(X);
").
@@ -660,11 +704,11 @@
%
% Pythagoras' number
:- pragma c_code(math__pi(Pi::out), [will_not_call_mercury, thread_safe],
- "Pi = MERCURY_FLOAT__PI;").
+ "Pi = ML_FLOAT_PI;").
% Base of natural logarithms
:- pragma c_code(math__e(E::out), [will_not_call_mercury, thread_safe],
- "E = MERCURY_FLOAT__E;").
+ "E = ML_FLOAT_E;").
%
% math__ceiling(X, Ceil) is true if Ceil is the smallest integer
@@ -699,9 +743,9 @@
:- pragma c_code(math__truncate(X::in, Trunc::out),
[will_not_call_mercury, thread_safe], "
if (X < 0.0) {
- Trunc = ceil(X);
+ Trunc = ceil(X);
} else {
- Trunc = floor(X);
+ Trunc = floor(X);
}
").
@@ -714,9 +758,11 @@
%
:- pragma c_code(math__sqrt(X::in, SquareRoot::out),
[will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X < 0.0) {
- mercury_domain_error(""math__sqrt"");
+ ML_math_domain_error(""math__sqrt"");
}
+#endif
SquareRoot = sqrt(X);
").
@@ -730,17 +776,21 @@
%
:- pragma c_code(math__pow(X::in, Y::in, Res::out),
[will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X < 0.0) {
- mercury_domain_error(""math__pow"");
+ ML_math_domain_error(""math__pow"");
}
if (X == 0.0) {
- if (Y <= 0.0) {
- mercury_domain_error(""math__pow"");
- }
- Res = 0.0;
+ if (Y <= 0.0) {
+ ML_math_domain_error(""math__pow"");
+ }
+ Res = 0.0;
} else {
- Res = pow(X, Y);
+ Res = pow(X, Y);
}
+#else
+ Res = pow(X, Y);
+#endif
").
%
@@ -761,9 +811,11 @@
%
:- pragma c_code(math__ln(X::in, Log::out),
[will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X <= 0.0) {
- mercury_domain_error(""math__ln"");
+ ML_math_domain_error(""math__ln"");
}
+#endif
Log = log(X);
").
@@ -776,8 +828,11 @@
%
:- pragma c_code(math__log10(X::in, Log10::out),
[will_not_call_mercury, thread_safe], "
- if (X <= 0.0)
- mercury_domain_error(""math__log10"");
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
+ if (X <= 0.0) {
+ ML_math_domain_error(""math__log10"");
+ }
+#endif
Log10 = log10(X);
").
@@ -790,10 +845,12 @@
%
:- pragma c_code(math__log2(X::in, Log2::out),
[will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X <= 0.0) {
- mercury_domain_error(""math__log2"");
+ ML_math_domain_error(""math__log2"");
}
- Log2 = log(X) / MERCURY_FLOAT__LN2;
+#endif
+ Log2 = log(X) / ML_FLOAT_LN2;
").
%
@@ -807,12 +864,14 @@
%
:- pragma c_code(math__log(B::in, X::in, Log::out),
[will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X <= 0.0 || B <= 0.0) {
- mercury_domain_error(""math__log"");
+ ML_math_domain_error(""math__log"");
}
if (B == 1.0) {
- mercury_domain_error(""math__log"");
+ ML_math_domain_error(""math__log"");
}
+#endif
Log = log(X)/log(B);
").
@@ -849,9 +908,11 @@
%
:- pragma c_code(math__asin(X::in, ASin::out),
[will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X < -1.0 || X > 1.0) {
- mercury_domain_error(""math__asin"");
+ ML_math_domain_error(""math__asin"");
}
+#endif
ASin = asin(X);
").
@@ -864,9 +925,11 @@
%
:- pragma c_code(math__acos(X::in, ACos::out),
[will_not_call_mercury, thread_safe], "
+#ifndef ML_OMIT_MATH_DOMAIN_CHECKS
if (X < -1.0 || X > 1.0) {
- mercury_domain_error(""math__acos"");
+ ML_math_domain_error(""math__acos"");
}
+#endif
ACos = asin(X);
").
More information about the developers
mailing list