Files
mercury/library/float.m
Fergus Henderson 9657e46668 Document the intended handling of IEEE NaNs and signed zeros.
Estimated hours taken: 4

library/float.m:
	Document the intended handling of IEEE NaNs and signed zeros.
	The idea is that the semantic inconsistencies between IEEE
	floating point and Mercury's declarative semantics should be
	resolved by saying for any operation where the two specify
	inconsistent results, executing that operation should result
	in a run-time error (or exception).
1998-07-26 14:41:58 +00:00

468 lines
12 KiB
Mathematica

%---------------------------------------------------------------------------%
% Copyright (C) 1994-1998 The University of Melbourne.
% This file may only be copied under the terms of the GNU Library General
% Public License - see the file COPYING.LIB in the Mercury distribution.
%---------------------------------------------------------------------------%
%
% File: float.m.
% Main author: fjh.
% Stability: medium.
%
% Floating point support.
%
% Note that implementations which support IEEE floating point
% should ensure that in cases where the only valid answer is a "NaN"
% (the IEEE float representation for "not a number"), the det
% functions here will halt with a runtime error (or throw an exception)
% rather than returning a NaN. Quiet (non-signalling) NaNs have a
% semantics which is not valid in Mercury, since they don't obey the
% axiom "all [X] X = X".
%
% XXX Unfortunately the current Mercury implementation does not
% do that on all platforms, since neither ANSI C nor POSIX provide
% any portable way of ensuring that floating point operations
% whose result is not representable will raise a signal rather
% than returning a NaN. (Maybe C9X will help...?)
% The behaviour is correct on Linux and Digital Unix,
% but not on Solaris, for example.
%
% IEEE floating point also specifies that some functions should
% return different results for +0.0 and -0.0, but that +0.0 and -0.0
% should compare equal. This semantics is not valid in Mercury,
% since it doesn't obey the axiom `all [F, X, Y] X = Y => F(X) = F(Y)'.
% Again, the resolution is that in Mercury, functions which would
% return different results for +0.0 and -0.0 should instead halt
% execution with a run-time error (or throw an exception).
%
% XXX Here too the current Mercury implementation does not
% implement the intended semantics correctly on all platforms.
%
%---------------------------------------------------------------------------%
:- module float.
:- interface.
%
% Arithmetic functions
%
% addition
:- func float + float = float.
:- mode in + in = uo is det.
:- mode uo + in = in is det.
:- mode in + uo = in is det.
% subtraction
:- func float - float = float.
:- mode in - in = uo is det.
:- mode uo - in = in is det.
:- mode in - uo = in is det.
% multiplication
:- func float * float = float.
:- mode in * in = uo is det.
:- mode uo * in = in is det.
:- mode in * uo = in is det.
% division
:- func float / float = float.
:- mode in / in = uo is det.
:- mode uo / in = in is det.
:- mode in / uo = in is det.
% unary plus
:- func + float = float.
:- mode + in = uo is det.
% unary minus
:- func - float = float.
:- mode - in = uo is det.
%
% Comparison predicates
%
% less than
:- pred <(float, float).
:- mode <(in, in) is semidet.
% greater than
:- pred >(float, float).
:- mode >(in, in) is semidet.
% less than or equal
:- pred =<(float, float).
:- mode =<(in, in) is semidet.
% greater than or equal
:- pred >=(float, float).
:- mode >=(in, in) is semidet.
%
% Conversion functions
%
% Convert int to float
:- func float(int) = float.
% ceiling_to_int(X) returns the
% smallest integer not less than X.
:- func ceiling_to_int(float) = int.
% floor_to_int(X) returns the
% largest integer not greater than X.
:- func floor_to_int(float) = int.
% round_to_int(X) returns the integer closest to X.
% If X has a fractional value of 0.5, it is rounded up.
:- func round_to_int(float) = int.
% truncate_to_int(X) returns
% the integer closest to X such that |truncate_to_int(X)| =< |X|.
:- func truncate_to_int(float) = int.
%
% Miscellaneous functions
%
% absolute value
:- func abs(float) = float.
% maximum
:- func max(float, float) = float.
% minimum
:- func min(float, float) = float.
% pow(Base, Exponent) returns Base raised to the power Exponent.
% The exponent must be an integer greater or equal to 0.
% Currently this function runs at O(n), where n is the value
% of the exponent.
:- func pow(float, int) = float.
% Compute a non-negative integer hash value for a float.
% (Note: the hash values computed by the Mercury implementation of
% this predicate are different to those computed by the Prolog
% implementation.)
:- func hash(float) = int.
%
% System constants
%
% Maximum floating-point number
:- func float__max = float.
% Minimum normalised floating-point number
:- func float__min = float.
% Smallest number x such that 1.0 + x \= 1.0
:- func float__epsilon = float.
%---------------------------------------------------------------------------%
% Predicate versions of the functions declared above.
% These are intended for use in programs that need to work
% in both Prolog and Mercury (e.g. for debugging).
% These predicate versions will eventually become obsolete.
%
% Conversion predicates
%
% float__ceiling_to_int(X, Ceil) is true if Ceil is the
% smallest integer not less than X.
:- pred float__ceiling_to_int(float, int).
:- mode float__ceiling_to_int(in, out) is det.
% float__floor_to_int(X, Ceil) is true if Ceil is the
% largest integer not greater than X.
:- pred float__floor_to_int(float, int).
:- mode float__floor_to_int(in, out) is det.
% 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.
:- pred float__round_to_int(float, int).
:- mode float__round_to_int(in, out) is det.
% float__truncate_to_int(X, Trunc) is true if Trunc is
% the integer closest to X such that |Trunc| =< |X|.
:- pred float__truncate_to_int(float, int).
:- mode float__truncate_to_int(in, out) is det.
%
% Miscellaneous predicates
%
% absolute value
:- pred float__abs(float, float).
:- mode float__abs(in, out) is det.
% maximum
:- pred float__max(float, float, float).
:- mode float__max(in, in, out) is det.
% minimum
:- pred float__min(float, float, float).
:- mode float__min(in, in, out) is det.
% float__pow(Base, Exponent, Answer) is true iff Answer is
% Base raised to the power Exponent. The exponent must be an
% integer greater or equal to 0. Currently this function runs
% at O(n), where n is the value of the exponent.
:- pred float__pow(float, int, float).
:- mode float__pow(in, in, out) is det.
% Compute a non-negative integer hash value for a float.
% (Note: the hash values computed by the Mercury implementation of
% this predicate are different to those computed by the Prolog
% implementation.)
:- pred float__hash(float, int).
:- mode float__hash(in, out) is det.
%
% System constant predicates
%
% Maximum floating-point number
:- pred float__max(float).
:- mode float__max(out) is det.
% Minimum normalised floating-point number
:- pred float__min(float).
:- mode float__min(out) is det.
% Smallest number x such that 1.0 + x \= 1.0
:- pred float__epsilon(float).
:- mode float__epsilon(out) is det.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- interface.
% Everything below here will not appear in the
% Mercury Library Reference Manual.
%---------------------------------------------------------------------------%
/* The following predicates are obsolete. Don't use them.
They will eventually disappear in some future release.
*/
% :- pragma obsolete(builtin_float_plus/3).
:- pred builtin_float_plus(float, float, float).
:- mode builtin_float_plus(in, in, uo) is det.
% :- pragma obsolete(builtin_float_minus/3).
:- pred builtin_float_minus(float, float, float).
:- mode builtin_float_minus(in, in, uo) is det.
% :- pragma obsolete(builtin_float_times/3).
:- pred builtin_float_times(float, float, float).
:- mode builtin_float_times(in, in, uo) is det.
% :- pragma obsolete(builtin_float_divide/3).
:- pred builtin_float_divide(float, float, float).
:- mode builtin_float_divide(in, in, uo) is det.
% :- pragma obsolete(builtin_float_gt/2).
:- pred builtin_float_gt(float, float).
:- mode builtin_float_gt(in, in) is semidet.
% :- pragma obsolete(builtin_float_lt/2).
:- pred builtin_float_lt(float, float).
:- mode builtin_float_lt(in, in) is semidet.
% :- pragma obsolete(builtin_float_ge/2).
:- pred builtin_float_ge(float, float).
:- mode builtin_float_ge(in, in) is semidet.
% :- pragma obsolete(builtin_float_le/2).
:- pred builtin_float_le(float, float).
:- mode builtin_float_le(in, in) is semidet.
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%
:- implementation.
:- import_module int, require.
%
% Header files of mathematical significance.
%
:- pragma c_header_code("
#include <float.h>
#include <math.h>
").
%---------------------------------------------------------------------------%
% The arithmetic and comparison operators are builtins,
% which the compiler expands inline. We don't need to define them here.
%---------------------------------------------------------------------------%
%
% Conversion functions
%
float(Int) = Float :-
int__to_float(Int, Float).
% 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,
"
Ceil = (Integer) ceil(X);
").
float__ceiling_to_int(X) = Ceil :- float__ceiling_to_int(X, Ceil).
% 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,
"
Floor = (Integer) floor(X);
").
float__floor_to_int(X) = Floor :- float__floor_to_int(X, Floor).
% 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,
"
Round = (Integer) floor(X + 0.5);
").
float__round_to_int(X) = Round :- float__round_to_int(X, Round).
% 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,
"
Trunc = (Integer) X;
").
float__truncate_to_int(X) = Trunc :- float__truncate_to_int(X, Trunc).
%---------------------------------------------------------------------------%
%
% Miscellaneous functions
%
float__abs(Num, Abs) :-
(
Num =< 0.0
->
Abs = - Num
;
Abs = Num
).
float__abs(Num) = Abs :- float__abs(Num, Abs).
float__max(X, Y, Max) :-
(
X >= Y
->
Max = X
;
Max = Y
).
float__max(X, Y) = Max :- float__max(X, Y, Max).
float__min(X, Y, Min) :-
(
X =< Y
->
Min = X
;
Min = Y
).
float__min(X, Y) = Min :- float__min(X, Y, Min).
% float_pow(Base, Exponent, Answer).
% XXXX This function could be more efficient, with an int_mod pred, to
% reduce O(N) to O(logN) of the exponent.
float__pow( X, Exp, Ans) :-
( Exp < 0 ->
error("float__pow taken with exponent < 0\n")
; Exp = 1 ->
Ans = X
; Exp = 0 ->
Ans = 1.0
;
New_e is Exp - 1,
float__pow(X, New_e, A2),
Ans is X * A2
).
float__pow(X, Exp) = Pow :- float__pow(X, Exp, Pow).
:- pragma c_code(
float__hash(F::in, H::out),
will_not_call_mercury,
"
H = hash_float(F);
").
float__hash(F) = H :- float__hash(F, H).
%---------------------------------------------------------------------------%
%
% System constants
%
% The floating-point system constants are derived from <float.h> and
% implemented using the C interface.
:- pragma c_header_code("
#if defined USE_SINGLE_PREC_FLOAT
#define MERCURY_FLOAT_MAX FLT_MAX
#define MERCURY_FLOAT_MIN FLT_MIN
#define MERCURY_FLOAT_EPSILON FLT_EPSILON
#else
#define MERCURY_FLOAT_MAX DBL_MAX
#define MERCURY_FLOAT_MIN DBL_MIN
#define MERCURY_FLOAT_EPSILON DBL_EPSILON
#endif
").
% Maximum floating-point number
:- pragma c_code(float__max(Max::out), will_not_call_mercury,
"Max = MERCURY_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;").
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;").
float__epsilon = Epsilon :- float__epsilon(Epsilon).
%---------------------------------------------------------------------------%
%---------------------------------------------------------------------------%