mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
We had configure choose the format string length modifier for
MR_int_least64_t based on whether the type is an alias for "int",
"long", "long long" or "__int64". That does not work for some versions
of MinGW-w64, which warn about the "ll" length modifier even though
MR_int_least64_t is an alias for "long long". The reason is that
we would be calling the MSVC runtime *printf functions,
which require the "I64" length modifier instead of "ll".
The only place MR_INT_LEAST64_LENGTH_MODIFIER is used in the Mercury
system is in the hidden function float64_bits_string (which is also
no longer used by the Mercury compiler after the removal of the hl
grades). We can replace that use with PRIdLEAST64 from inttypes.h.
Users are unlikely to be using MR_INT_LEAST64_LENGTH_MODIFIER,
so it should be safe to remove it.
configure.ac:
runtime/mercury_conf.h.in:
Don't define MR_INT_LEAST64_LENGTH_MODIFIER.
library/float.m:
Replace use of MR_INT_LEAST64_LENGTH_MODIFIER with PRIdLEAST64
in float64_bits_string.
Mark all float32_bits_string and float64_bits_string foreign procs
as 'may_not_export_body'. There is no need to opt-export procedures
that will rarely be used, if ever.
runtime/mercury_types.h:
Update comment.
1239 lines
36 KiB
Mathematica
1239 lines
36 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1994-1998,2001-2008,2010, 2012 The University of Melbourne.
|
|
% Copyright (C) 2013-2016, 2018-2022 The Mercury team.
|
|
% This file is distributed under the terms specified in COPYING.LIB.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: float.m.
|
|
% Main author: fjh.
|
|
% Stability: medium.
|
|
%
|
|
% Floating point support.
|
|
%
|
|
% Floats are double precision, except in .spf grades where they
|
|
% are single precision.
|
|
%
|
|
% 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.
|
|
%
|
|
% XXX On machines such as x86 which support extra precision
|
|
% for intermediate results, the results may depend on the
|
|
% level of optimization, in particular inlining and evaluation
|
|
% of constant expressions.
|
|
% For example, the goal `1.0/9.0 = std_util.id(1.0)/9.0' may fail.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module float.
|
|
:- interface.
|
|
|
|
:- import_module pretty_printer.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Arithmetic functions.
|
|
%
|
|
|
|
% Addition.
|
|
%
|
|
:- func (float::in) + (float::in) = (float::uo) is det.
|
|
|
|
% Subtraction.
|
|
%
|
|
:- func (float::in) - (float::in) = (float::uo) is det.
|
|
|
|
% Multiplication.
|
|
%
|
|
:- func (float::in) * (float::in) = (float::uo) is det.
|
|
|
|
% Division.
|
|
% Throws a `domain_error' exception if the right operand is zero.
|
|
% See the comments at the top of math.m to find out how to disable
|
|
% this check.
|
|
%
|
|
:- func (float::in) / (float::in) = (float::uo) is det.
|
|
|
|
% unchecked_quotient(X, Y) is the same as X / Y, but the behaviour
|
|
% is undefined if the right operand is zero.
|
|
%
|
|
:- func unchecked_quotient(float::in, float::in) = (float::uo) is det.
|
|
|
|
% Unary plus.
|
|
%
|
|
:- func + (float::in) = (float::uo) is det.
|
|
|
|
% Unary minus.
|
|
%
|
|
:- func - (float::in) = (float::uo) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Comparison predicates.
|
|
%
|
|
|
|
% Less than.
|
|
%
|
|
:- pred (float::in) < (float::in) is semidet.
|
|
|
|
% Less than or equal.
|
|
%
|
|
:- pred (float::in) =< (float::in) is semidet.
|
|
|
|
% Greater than or equal.
|
|
%
|
|
:- pred (float::in) >= (float::in) is semidet.
|
|
|
|
% Greater than.
|
|
%
|
|
:- pred (float::in) > (float::in) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Conversion from integer types.
|
|
%
|
|
|
|
% Convert an int into float.
|
|
%
|
|
% The behaviour when the int exceeds the range of what can be exactly
|
|
% represented by a float is undefined.
|
|
%
|
|
:- func float(int) = float.
|
|
|
|
% Convert a signed 8-bit integer into a float.
|
|
% Always succeeds as all signed 8-bit integers have an exact
|
|
% floating-point representation.
|
|
%
|
|
:- func from_int8(int8) = float.
|
|
|
|
% Convert a signed 16-bit integer into a float.
|
|
% Always succeeds as all signed 16-bit integers have an exact
|
|
% floating-point representation.
|
|
%
|
|
:- func from_int16(int16) = float.
|
|
|
|
% Convert a signed 32-bit integer into a float.
|
|
% The behaviour when the integer exceeds the range of what can be
|
|
% exactly represented by a float is undefined.
|
|
%
|
|
:- func cast_from_int32(int32) = float.
|
|
|
|
% Convert a signed 64-bit integer into a float.
|
|
% The behaviour when the integer exceeds the range of what can be
|
|
% exactly represented by a float is undefined.
|
|
%
|
|
:- func cast_from_int64(int64) = float.
|
|
|
|
% Convert an unsigned 8-bit integer into a float.
|
|
% Always succeeds as all unsigned 8-bit integers have an exact
|
|
% floating-point representation.
|
|
%
|
|
:- func from_uint8(uint8) = float.
|
|
|
|
% Convert an unsigned 16-bit integer into a float.
|
|
% Always succeeds as all unsigned 16-bit integers have an exact
|
|
% floating-point representation.
|
|
%
|
|
:- func from_uint16(uint16) = float.
|
|
|
|
% Convert an unsigned 32-bit integer into a float.
|
|
% The behaviour when the integer exceeds the range of what can be
|
|
% exactly represented by a float is undefined.
|
|
%
|
|
:- func cast_from_uint32(uint32) = float.
|
|
|
|
% Convert an unsigned 64-bit integer into a float.
|
|
% The behaviour when the integer exceeds the range of what can be
|
|
% exactly represented by a float is undefined.
|
|
%
|
|
:- func cast_from_uint64(uint64) = float.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Conversion to int.
|
|
%
|
|
|
|
% 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.
|
|
% Fewer domain restrictions than math.pow: works for negative Base,
|
|
% and pow(B, 0) = 1.0 for all B, even B=0.0.
|
|
% Only pow(0, <negative>) throws a `domain_error' exception.
|
|
%
|
|
:- func pow(float, int) = float.
|
|
|
|
% Compute a non-negative integer hash value for a float.
|
|
%
|
|
:- func hash(float) = int.
|
|
:- pred hash(float::in, int::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Classification.
|
|
%
|
|
|
|
% True iff the argument is of infinite magnitude.
|
|
%
|
|
:- pred is_infinite(float::in) is semidet.
|
|
|
|
% Synonym for the above.
|
|
%
|
|
:- pred is_inf(float::in) is semidet.
|
|
|
|
% True iff the argument is not-a-number (NaN).
|
|
%
|
|
:- pred is_nan(float::in) is semidet.
|
|
|
|
% True iff the argument is of infinite magnitude or not-a-number (NaN).
|
|
%
|
|
:- pred is_nan_or_infinite(float::in) is semidet.
|
|
|
|
% Synonym for the above.
|
|
%
|
|
:- pred is_nan_or_inf(float::in) is semidet.
|
|
|
|
% True iff the argument is not of infinite magnitude and is not a
|
|
% not-a-number (NaN) value.
|
|
%
|
|
:- pred is_finite(float::in) is semidet.
|
|
|
|
% True iff the argument is of zero magnitude.
|
|
%
|
|
:- pred is_zero(float::in) is semidet.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% System constants.
|
|
%
|
|
|
|
% Maximum finite floating-point number.
|
|
%
|
|
% max = (1 - radix ** mantissa_digits) * radix ** max_exponent
|
|
%
|
|
:- func max = float.
|
|
|
|
% Minimum normalised positive floating-point number.
|
|
%
|
|
% min = radix ** (min_exponent - 1)
|
|
%
|
|
:- func min = float.
|
|
|
|
% Positive infinity.
|
|
%
|
|
:- func infinity = 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 epsilon = float.
|
|
|
|
% Radix of the floating-point representation.
|
|
% In the literature, this is sometimes referred to as `b'.
|
|
%
|
|
:- func radix = int.
|
|
|
|
% The number of base-radix digits in the mantissa.
|
|
% In the literature, this is sometimes referred to as `p' or `t'.
|
|
%
|
|
:- func 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 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 max_exponent = int.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Convert a float to a pretty_printer.doc for formatting.
|
|
%
|
|
:- func float_to_doc(float) = doc.
|
|
:- pragma obsolete(func(float_to_doc/1), [pretty_printer.float_to_doc/1]).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- interface.
|
|
|
|
% These functions are hidden for now. `int' is not guaranteed to be able to
|
|
% represent a double, so we return strings for now. Endianness and treatment
|
|
% of special values also needs to be considered.
|
|
|
|
% Convert a float to an IEEE single-precision floating point value, then
|
|
% return the integer representation of the bit layout of that value as a
|
|
% string.
|
|
%
|
|
:- func float32_bits_string(float::in) = (string::uo) is det.
|
|
|
|
% Convert a float to an IEEE double-precision floating point value, then
|
|
% return the integer representation of the bit layout of that value as a
|
|
% string.
|
|
%
|
|
:- func float64_bits_string(float::in) = (string::uo) is det.
|
|
|
|
:- implementation.
|
|
|
|
:- import_module exception.
|
|
:- import_module int.
|
|
|
|
:- pragma foreign_decl("C", "
|
|
#include <float.h>
|
|
#include <inttypes.h>
|
|
#include <math.h>
|
|
|
|
#ifdef MR_HAVE_IEEEFP_H
|
|
#include <ieeefp.h>
|
|
#endif
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% The other arithmetic and comparison operators are builtins,
|
|
% which the compiler expands inline. We don't need to define them here.
|
|
|
|
:- pragma inline(func('/'/2)).
|
|
X / Y = Z :-
|
|
( if float_domain_checks, Y = 0.0 then
|
|
throw(domain_error("float.'/': division by zero"))
|
|
else
|
|
Z = unchecked_quotient(X, Y)
|
|
).
|
|
|
|
% This code is included here rather than just calling the version in
|
|
% math.m because we currently don't do transitive inter-module inlining,
|
|
% so code which uses `/'/2 but doesn't import math.m couldn't have the
|
|
% domain check optimized away.
|
|
%
|
|
:- pred float_domain_checks is semidet.
|
|
:- pragma inline(pred(float_domain_checks/0)).
|
|
|
|
:- pragma foreign_proc("C",
|
|
float_domain_checks,
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
#ifdef ML_OMIT_MATH_DOMAIN_CHECKS
|
|
SUCCESS_INDICATOR = MR_FALSE;
|
|
#else
|
|
SUCCESS_INDICATOR = MR_TRUE;
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
float_domain_checks,
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
#if ML_OMIT_MATH_DOMAIN_CHECKS
|
|
SUCCESS_INDICATOR = false;
|
|
#else
|
|
SUCCESS_INDICATOR = true;
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
float_domain_checks,
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
SUCCESS_INDICATOR = true;
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Conversion from integer types.
|
|
%
|
|
|
|
% For Java, overflows are not detected, so this must be tested for
|
|
% explicitly. So every time there's a cast to int, the bounds are
|
|
% checked first.
|
|
|
|
:- pragma foreign_proc("C",
|
|
float(IntVal::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
FloatVal = IntVal;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
float(IntVal::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) IntVal;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
float(IntVal::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) IntVal;
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
from_int8(Int8Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
FloatVal = Int8Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
from_int8(Int8Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) Int8Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
from_int8(Int8Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) Int8Val;
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
from_uint8(UInt8Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
FloatVal = UInt8Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
from_uint8(UInt8Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) UInt8Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
from_uint8(UInt8Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) (UInt8Val & 0xff);
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
from_int16(Int16Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
FloatVal = Int16Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
from_int16(Int16Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) Int16Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
from_int16(Int16Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) Int16Val;
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
from_uint16(UInt16Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
FloatVal = UInt16Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
from_uint16(UInt16Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) UInt16Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
from_uint16(UInt16Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) (UInt16Val & 0xffff);
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
cast_from_int32(Int32Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
FloatVal = Int32Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
cast_from_int32(Int32Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) Int32Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
cast_from_int32(Int32Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) Int32Val;
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
cast_from_uint32(UInt32Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
FloatVal = UInt32Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
cast_from_uint32(UInt32Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) UInt32Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
cast_from_uint32(UInt32Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) (UInt32Val & 0xffffffff);
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
cast_from_int64(Int64Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
FloatVal = Int64Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
cast_from_int64(Int64Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) Int64Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
cast_from_int64(Int64Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) Int64Val;
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
cast_from_uint64(UInt64Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
FloatVal = UInt64Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
cast_from_uint64(UInt64Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) UInt64Val;
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
cast_from_uint64(UInt64Val::in) = (FloatVal::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
FloatVal = (double) UInt64Val;
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Conversion to ints.
|
|
%
|
|
|
|
:- pragma foreign_proc("C",
|
|
ceiling_to_int(X::in) = (Ceil::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe,
|
|
does_not_affect_liveness],
|
|
"
|
|
Ceil = (MR_Integer) ML_FLOAT_CEIL(X);
|
|
").
|
|
:- pragma foreign_proc("C#",
|
|
ceiling_to_int(X::in) = (Ceil::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
Ceil = System.Convert.ToInt32(System.Math.Ceiling(X));
|
|
").
|
|
:- pragma foreign_proc("Java",
|
|
ceiling_to_int(X::in) = (Ceil::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
if (X > (double) java.lang.Integer.MAX_VALUE ||
|
|
X <= (double) java.lang.Integer.MIN_VALUE - 1)
|
|
{
|
|
throw new java.lang.RuntimeException(
|
|
""Overflow converting floating point to int"");
|
|
} else {
|
|
Ceil = (int) java.lang.Math.ceil(X);
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
floor_to_int(X::in) = (Floor::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
Floor = (MR_Integer) ML_FLOAT_FLOOR(X);
|
|
").
|
|
:- pragma foreign_proc("C#",
|
|
floor_to_int(X::in) = (Floor::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
Floor = System.Convert.ToInt32(System.Math.Floor(X));
|
|
").
|
|
:- pragma foreign_proc("Java",
|
|
floor_to_int(X :: in) = (Floor :: out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
if (X >= (double) java.lang.Integer.MAX_VALUE + 1 ||
|
|
X < (double) java.lang.Integer.MIN_VALUE)
|
|
{
|
|
throw new java.lang.RuntimeException(
|
|
""Overflow converting floating point to int"");
|
|
} else {
|
|
Floor = (int) java.lang.Math.floor(X);
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
round_to_int(X::in) = (Round::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
Round = (MR_Integer) ML_FLOAT_FLOOR(X + (MR_Float)0.5);
|
|
").
|
|
:- pragma foreign_proc("C#",
|
|
round_to_int(X::in) = (Round::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
Round = System.Convert.ToInt32(System.Math.Floor(X + 0.5));
|
|
").
|
|
:- pragma foreign_proc("Java",
|
|
round_to_int(X::in) = (Round::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
if (X >= (double) java.lang.Integer.MAX_VALUE + 0.5 ||
|
|
X < (double) java.lang.Integer.MIN_VALUE - 0.5)
|
|
{
|
|
throw new java.lang.RuntimeException(
|
|
""Overflow converting floating point to int"");
|
|
} else {
|
|
Round = (int) java.lang.Math.round(X);
|
|
}
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
truncate_to_int(X::in) = (Trunc::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
Trunc = (MR_Integer) X;
|
|
").
|
|
:- pragma foreign_proc("C#",
|
|
truncate_to_int(X::in) = (Trunc::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
Trunc = System.Convert.ToInt32(X);
|
|
").
|
|
:- pragma foreign_proc("Java",
|
|
truncate_to_int(X::in) = (Trunc::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
if (X >= (double) java.lang.Integer.MAX_VALUE + 1 ||
|
|
X <= (double) java.lang.Integer.MIN_VALUE - 1)
|
|
{
|
|
throw new java.lang.RuntimeException(
|
|
""Overflow converting floating point to int"");
|
|
} else {
|
|
Trunc = (int) X;
|
|
}
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Miscellaneous functions.
|
|
%
|
|
|
|
abs(Num) = Abs :-
|
|
( if Num =< 0.0 then
|
|
Abs = - Num
|
|
else
|
|
Abs = Num
|
|
).
|
|
|
|
max(X, Y) = Max :-
|
|
( if X >= Y then
|
|
Max = X
|
|
else
|
|
Max = Y
|
|
).
|
|
|
|
min(X, Y) = Min :-
|
|
( if X =< Y then
|
|
Min = X
|
|
else
|
|
Min = Y
|
|
).
|
|
|
|
pow(Base, Exp) = Ans :-
|
|
( if Exp >= 0 then
|
|
Ans = multiply_by_pow(1.0, Base, Exp)
|
|
else
|
|
( if float_domain_checks, Base = 0.0 then
|
|
throw(domain_error("float.pow: zero base"))
|
|
else
|
|
Ans = unchecked_quotient(1.0,
|
|
multiply_by_pow(1.0, Base, -Exp))
|
|
% See below re use of unchecked_quotient.
|
|
)
|
|
).
|
|
|
|
% Returns Scale0 * (Base ** Exp) (where X ** 0 == 1.0 for all X).
|
|
% Requires that Exp >= 0.
|
|
% Uses a simple "Russian peasants" algorithm. O(lg(Exp+1)).
|
|
%
|
|
:- func multiply_by_pow(float, float, int) = float.
|
|
|
|
multiply_by_pow(Scale0, Base, Exp) = Result :-
|
|
( if Exp = 0 then
|
|
Result = Scale0
|
|
else
|
|
( if odd(Exp) then
|
|
Scale1 = Scale0 * Base
|
|
else
|
|
Scale1 = Scale0
|
|
),
|
|
Result = multiply_by_pow(Scale1, Base * Base, Exp div 2)
|
|
).
|
|
|
|
% The reason for using unchecked_quotient in float.pow is so
|
|
% that float.pow(+/-0.5, -1111) gives +/-infinity rather than
|
|
% a domain error. (N.B. This relies on unchecked_quotient(1.0,
|
|
% +/-0.0) giving +/-infinity, whereas the documentation in
|
|
% float.m says that the results are undefined.)
|
|
% Using Result = float.multiply_by_pow(1.0, 1.0 / Base, -Exp)
|
|
% would give the right behaviour for underflow, but isn't
|
|
% generally as accurate.
|
|
|
|
% (Efficiency note: An optimization used by `power' in SGI's STL
|
|
% implementation is to test for Exp=0 and (for non-zero Exp) handle
|
|
% low zero bits in Exp before calling this loop: the loop for the low
|
|
% zero bits needs only square Base, it needn't update Acc until the
|
|
% end of that loop at which point Acc can be simply assigned from the
|
|
% then-current value of Base. This optimization would be especially
|
|
% valuable for expensive `*' operations; maybe provide a
|
|
% std_util.monoid_pow(func(T,T)=T MonoidOperator, T Identity, int
|
|
% Exp, T Base) = T Result function to complement the existing
|
|
% std_util.pow function.)
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% In hashing a float in .NET or Java, we ensure that the value is
|
|
% non-negative, as this condition is not guaranteed by either API.
|
|
:- pragma foreign_proc("C",
|
|
hash(F::in) = (H::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
H = MR_hash_float(F);
|
|
").
|
|
:- pragma foreign_proc("C#",
|
|
hash(F::in) = (H::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
int Code = F.GetHashCode();
|
|
if (Code < 0) {
|
|
H = -Code ^ 1;
|
|
} else {
|
|
H = Code;
|
|
}
|
|
").
|
|
:- pragma foreign_proc("Java",
|
|
hash(F::in) = (H::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
int Code = java.lang.Double.valueOf(F).hashCode();
|
|
if (Code < 0) {
|
|
H = -Code ^ 1;
|
|
} else {
|
|
H = Code;
|
|
}
|
|
").
|
|
|
|
hash(F, H) :-
|
|
H = hash(F).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
is_infinite(F) :-
|
|
is_inf(F).
|
|
|
|
is_nan_or_infinite(Float) :-
|
|
is_nan_or_inf(Float).
|
|
|
|
is_nan_or_inf(Float) :-
|
|
( is_nan(Float)
|
|
; is_inf(Float)
|
|
).
|
|
|
|
:- pragma foreign_proc("C",
|
|
is_nan(Flt::in),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
SUCCESS_INDICATOR = MR_is_nan(Flt);
|
|
").
|
|
:- pragma foreign_proc("C#",
|
|
is_nan(Flt::in),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
SUCCESS_INDICATOR = System.Double.IsNaN(Flt);
|
|
").
|
|
:- pragma foreign_proc("Java",
|
|
is_nan(Flt::in),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
SUCCESS_INDICATOR = java.lang.Double.isNaN(Flt);
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
is_inf(Flt::in),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
SUCCESS_INDICATOR = MR_is_infinite(Flt);
|
|
").
|
|
:- pragma foreign_proc("C#",
|
|
is_inf(Flt::in),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
SUCCESS_INDICATOR = System.Double.IsInfinity(Flt);
|
|
").
|
|
:- pragma foreign_proc("Java",
|
|
is_inf(Flt::in),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
SUCCESS_INDICATOR = java.lang.Double.isInfinite(Flt);
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
is_finite(Flt::in),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
SUCCESS_INDICATOR = MR_is_finite(Flt);
|
|
").
|
|
|
|
is_finite(F) :-
|
|
not is_infinite(F),
|
|
not is_nan(F).
|
|
|
|
is_zero(0.0).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% System constants.
|
|
%
|
|
|
|
% For C, the floating-point system constants are derived from <float.h> and
|
|
% implemented using the C interface.
|
|
%
|
|
% For .NET (and Java), the values are mostly hard-coded.
|
|
% It's OK to do this, because the ECMA specification for the .NET CLR
|
|
% nails down the representation of System.Double as 64-bit IEEE float.
|
|
% The Java Language Specification also follows the IEEE standard.
|
|
%
|
|
% Many of these values are coded in Mercury clauses,
|
|
% rather than foreign_proc pragmas; assuming IEEE floating point
|
|
% is a reasonable default these days, and doing that might improve
|
|
% the compiler's optimization.
|
|
|
|
:- pragma foreign_decl("C",
|
|
"
|
|
#define ML_FLOAT_RADIX FLT_RADIX // There is no DBL_RADIX.
|
|
|
|
#if defined(MR_USE_SINGLE_PREC_FLOAT)
|
|
#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 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
|
|
|
|
#if defined(MR_USE_SINGLE_PREC_FLOAT)
|
|
#define ML_FLOAT_FLOOR(X) floorf(X)
|
|
#define ML_FLOAT_CEIL(X) ceilf(X)
|
|
#else
|
|
#define ML_FLOAT_FLOOR(X) floor(X)
|
|
#define ML_FLOAT_CEIL(X) ceil(X)
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
max = (Max::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
Max = ML_FLOAT_MAX;
|
|
").
|
|
:- pragma foreign_proc("C#",
|
|
max = (Max::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
Max = System.Double.MaxValue;
|
|
").
|
|
:- pragma foreign_proc("Java",
|
|
max = (Max::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
Max = java.lang.Double.MAX_VALUE;
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
min = (Min::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
Min = ML_FLOAT_MIN;
|
|
").
|
|
% C#:
|
|
% We can't use System.Double.MinValue, because in v1 of the .NET CLR
|
|
% that means something completely different: the negative number
|
|
% with the greatest absolute value.
|
|
% Instead, we just hard-code the appropriate value (copied from the
|
|
% glibc header files); this is OK, because the ECMA specification
|
|
% nails down the representation of double as 64-bit IEEE.
|
|
%
|
|
% Java:
|
|
% The Java API's java.lang.Double.MIN_VALUE is not normalized,
|
|
% so can't be used, so we use the same constant as for .NET, as the
|
|
% Java Language Specification also describes Java doubles as 64 bit IEEE.
|
|
%
|
|
min = 2.2250738585072014e-308.
|
|
|
|
% Java and C# provide constants for +infinity.
|
|
% For C, the situation is more complicated.
|
|
% For single precision floats, we use the following
|
|
% (1) the C99 INFINITY macro if available
|
|
% (2) the HUGE_VALF macro if available
|
|
% (3) HUGE_VAL
|
|
%
|
|
% For double precision floats we just use the HUGE_VAL macro.
|
|
% The C standard does not guarantee that this will be +infinity,
|
|
% but it actually is on all the systems that we support. It will definitely be
|
|
% +infinity if the optional annex F of C99 is supported or if the 2001
|
|
% revision of POSIX is supported.
|
|
|
|
:- pragma foreign_proc("C",
|
|
infinity = (F::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
#if defined(MR_USE_SINGLE_PREC_FLOAT)
|
|
#if defined(INFINITY)
|
|
F = INFINITY;
|
|
#elif defined(HUGE_VALF)
|
|
F = HUGE_VALF;
|
|
#else
|
|
F = HUGE_VAL;
|
|
#endif
|
|
#else
|
|
F = HUGE_VAL;
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
infinity = (F::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
F = java.lang.Double.POSITIVE_INFINITY;
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
infinity = (F::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe],
|
|
"
|
|
F = System.Double.PositiveInfinity;
|
|
").
|
|
|
|
infinity = _ :-
|
|
private_builtin.sorry(
|
|
"infinity/0 not currently available for this backend").
|
|
|
|
:- pragma foreign_proc("C",
|
|
epsilon = (Eps::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
Eps = ML_FLOAT_EPSILON;
|
|
").
|
|
% C#:
|
|
% We can't use System.Double.Epsilon, because in v1 of the .NET CLR,
|
|
% that means something completely different: the smallest (denormal)
|
|
% positive number. I don't know what the people who designed that
|
|
% were smoking; that semantics for 'epsilon' is different from the
|
|
% use of 'epsilon' in C, Lisp, Ada, etc., not to mention Mercury.
|
|
% Instead, we just hard-code the appropriate value (copied from the
|
|
% glibc header files); this is OK, because the ECMA specification
|
|
% nails down the representation of double as 64-bit IEEE.
|
|
%
|
|
% Java:
|
|
% The Java API doesn't provide an epsilon constant, so we use the
|
|
% same constant, which is ok since Java defines doubles as 64 bit IEEE.
|
|
%
|
|
epsilon = 2.2204460492503131e-16.
|
|
|
|
:- pragma foreign_proc("C",
|
|
radix = (Radix::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
Radix = ML_FLOAT_RADIX;
|
|
").
|
|
% C#:
|
|
% The ECMA specification requires that double be 64-bit IEEE.
|
|
% I think that implies that it must have Radix = 2.
|
|
% This is definitely right for x86, anyway.
|
|
%
|
|
% Java:
|
|
% The Java API doesn't provide this constant either, so we default to the
|
|
% same constant as .NET, which is ok since Java defines doubles as 64 bit
|
|
% IEEE.
|
|
%
|
|
radix = 2.
|
|
|
|
:- pragma foreign_proc("C",
|
|
mantissa_digits = (MantDig::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
MantDig = ML_FLOAT_MANT_DIG;
|
|
").
|
|
% C#:
|
|
% ECMA specifies that System.Double is 64-bit IEEE float
|
|
%
|
|
% Java:
|
|
% The Java API doesn't provide this constant either, so we default to the
|
|
% same constant as .NET, which is ok since Java defines doubles as 64 bit
|
|
% IEEE.
|
|
%
|
|
mantissa_digits = 53.
|
|
|
|
:- pragma foreign_proc("C",
|
|
min_exponent = (MinExp::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
MinExp = ML_FLOAT_MIN_EXP;
|
|
").
|
|
% C#:
|
|
% ECMA specifies that System.Double is 64-bit IEEE float
|
|
%
|
|
% Java:
|
|
% The Java API doesn't provide this constant either, so we default to the
|
|
% same constant as .NET, which is ok since Java defines doubles as 64 bit
|
|
% IEEE.
|
|
%
|
|
min_exponent = -1021.
|
|
|
|
:- pragma foreign_proc("C",
|
|
max_exponent = (MaxExp::out),
|
|
[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
|
|
does_not_affect_liveness],
|
|
"
|
|
MaxExp = ML_FLOAT_MAX_EXP;
|
|
").
|
|
% C#:
|
|
% ECMA specifies that System.Double is 64-bit IEEE float
|
|
%
|
|
% Java:
|
|
% The Java API doesn't provide this constant either, so we default to the
|
|
% same constant as .NET, which is ok since Java defines doubles as 64 bit
|
|
% IEEE.
|
|
%
|
|
max_exponent = 1024.
|
|
|
|
float_to_doc(F) = pretty_printer.float_to_doc(F).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pragma foreign_proc("C",
|
|
float32_bits_string(Flt::in) = (Str::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, may_not_export_body],
|
|
"
|
|
union {
|
|
float f;
|
|
MR_int_least32_t i;
|
|
} u;
|
|
char buf[64];
|
|
|
|
u.f = (float) Flt;
|
|
sprintf(buf, ""%d"", u.i);
|
|
MR_make_aligned_string_copy(Str, buf);
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
float32_bits_string(Flt::in) = (Str::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, may_not_export_body],
|
|
"
|
|
float f = (float) Flt;
|
|
int i = Float.floatToIntBits(f);
|
|
Str = Integer.toString(i);
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
float32_bits_string(Flt::in) = (Str::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, may_not_export_body],
|
|
"
|
|
float f = (float) Flt;
|
|
int i = System.BitConverter.ToInt32(System.BitConverter.GetBytes(f), 0);
|
|
Str = i.ToString();
|
|
").
|
|
|
|
:- pragma foreign_proc("C",
|
|
float64_bits_string(Flt::in) = (Str::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, may_not_export_body],
|
|
"
|
|
#if defined(MR_INT_LEAST64_TYPE)
|
|
|
|
union {
|
|
double f;
|
|
MR_int_least64_t i;
|
|
} u;
|
|
char buf[64];
|
|
|
|
u.f = (double) Flt;
|
|
sprintf(buf, ""%"" PRIdLEAST64, u.i);
|
|
MR_make_aligned_string_copy(Str, buf);
|
|
#else
|
|
MR_fatal_error(
|
|
""64-bit integers not supported on this platform"");
|
|
#endif
|
|
").
|
|
|
|
:- pragma foreign_proc("Java",
|
|
float64_bits_string(Flt::in) = (Str::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, may_not_export_body],
|
|
"
|
|
double d = (double) Flt;
|
|
long i = Double.doubleToLongBits(d);
|
|
Str = Long.toString(i);
|
|
").
|
|
|
|
:- pragma foreign_proc("C#",
|
|
float64_bits_string(Flt::in) = (Str::uo),
|
|
[will_not_call_mercury, promise_pure, thread_safe, may_not_export_body],
|
|
"
|
|
double d = (double) Flt;
|
|
long i = System.BitConverter.DoubleToInt64Bits(d);
|
|
Str = i.ToString();
|
|
").
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module float.
|
|
%---------------------------------------------------------------------------%
|