Status of unit testing of IEEE support on 8.1.22.
I’ve generated over 300 individual tests covering the new float_XXX flags and arithmetic functions. In general things look pretty good. Basic advertised functionality all works and no backwards compatibility issues detected to this point, i.e., things don’t change if you don’t modify the default settings.
54 assertion level tests fail, usually when the arguments are special IEEE values. A significant percentage of the failures are due to signed NaNs which are discussed further below, with other discussion items.
I have issued a pull request to add test_IEEE754.pl
to the swipl Tests/
directory.
- Single NaN value from Prolog arithmetic.
AFAIK, the IEEE 754 (and IEC 60559) standard says (from a stackoverflow discussion):
“Conversion of a quiet NaN in a supported format to an external character sequence shall produce a language-defined one of “nan” or a sequence that is equivalent except for case (e.g., “NaN”), with an optional preceding sign. (This standard does not interpret the sign of a NaN.)”
So your platform’s conversion functions may print the “sign” of NaN values, but it has no meaning, and you shouldn’t consider it for the purposes of testing.
Edited to be a bit stronger: it is almost always a bug to attach meaning to the “sign bit” of a NaN datum.
The C math libraries are a bit sloppy about this and return either NaN or -NaN with no obvious meaning to the sign.
IEEE NaN’s also contain 53 bits of mantissa with no defined standard meaning. I’ve seen reports of various uses of this (additional diagnostic info, whole type value systems, …) but these are things that are not supported by the C math libraries and I see no reason why they should be accommodated in Prolog arithmetic either. (Prolog has better, more transparent ways of doing this kind of thing, rather than burying them in binary data structures intended for other uses.)
So I propose that the result of any arithmetic evaluation that contains a function evaluation which produces a NaN is the single value of the nan
function. In particular it will never produce -nan
. This simplifies testing, e.g., 1.5NaN is -nan
is true, as is 1.5NaN is 0/0.0
. (Another way of doing this test is X is Exp, X=\=X
which is only true if X is a NaN
value.)
- Standard term order of NaN’s.
I would hope that NaN’s don’t live long enough in applications that this becomes an issue, but we need to decide where NaN’s fit in the standard term order. Currently numbers are sorted numerically, but that’s a little tough to do with NaN’s where all arithmetic comparisons other than inequality are false. For term comparisons (only) I propose that 1.5NaN
is strictly less than any other number, so they fit between logic variables and -1.0Inf
.
- Continuation values for integer and rational expressions.
IMO integer, rational, and float arithmetic should, as much as possible, have the same semantic behaviour. So anything that takes a float value should accept an integer or rational value and produce approximately the same result. This should also apply to continuation values -inf, inf
, and nan
. Examples:
1/0.0 =:= inf
and 1/0 =:=inf
ceil(inf) =:= inf
nan is 0.0*inf
and nan is 0*inf
The whole idea behind special IEEE continuation values is that arithmetic evaluation over any numeric type never causes exceptions.
- ceiling/floor/round/truncate
Currently these return a somewhat nonsensical integer for the IEEE specials, except for round which traps. For proper semantics an IEEE special should be the result, e.g., 1.0Inf is ceiling(inf)
, which means that result of these functions can be a float.I don’t think this should be very disruptive; it’s just the price you pay for polymorphic arithmetic.
- Summary of individual tests (other than FAILs due to signed NaN):
iEEE_flags
- Eclipse compatibility, float_rounding
value, nearest
or to_nearest
?
iEEE_cmp
- FAIL not(nan =< nan)
- FAIL not(nan >= nan)
- FAIL not(nan =< 0.0)
- FAIL not(nan >= 0.0)
iEEE_tcmp
- FAIL (compare(O,1.5NaN,0.0),compare(O,1.5NaN,0 ))
iEEE_div (these all produce infinities)
- FAIL 1.5NaN is -0.0/ 0.0
- FAIL 1.5NaN is -0.0/ -0.0
- FAIL 1.5NaN is 0.0/ 0.0
- FAIL 1.5NaN is 0.0/ -0.0
iEEE_sign
- FAIL 1.5NaN is sign(nan)
iEEE_log
- FAIL -1.0Inf is log( 0.0)
- FAIL -1.0Inf is log(-0.0)
iEEE_log
- FAIL -1.0Inf is log10( 0.0)
- FAIL -1.0Inf is log10(-0.0)
iEEE_pow
- FAIL 1.0 is -inf** 0.0
- FAIL 1.0 is -inf** -0.0
- FAIL 0.0 is -inf** -2
- FAIL 1.0Inf is -inf** 2
iEEE_lgamma
- FAIL 1.0Inf is lgamma(-0.0)
iEEE_ceil
- FAIL 1.0Inf is ceiling( inf)
- FAIL -1.0Inf is ceiling(-inf)
- FAIL 1.5NaN is ceiling( nan)
iEEE_floor
- FAIL 1.0Inf is floor( inf)
- FAIL -1.0Inf is floor(-inf)
- FAIL 1.5NaN is floor( nan)
iEEE_round
- FAIL 1.0Inf is round( inf)
- FAIL -1.0Inf is round(-inf)
- FAIL 1.5NaN is round( nan)
iEEE_trunc
- FAIL 1.0Inf is truncate( inf)
- FAIL -1.0Inf is truncate(-inf)
- FAIL 1.5NaN is truncate( nan)
iEEE_max
- FAIL 0.0 is max(-0.0, 0.0)
- FAIL -1.0Inf is max( nan, -inf)
iEEE_min
- FAIL 0.0 is max(-0.0, 0.0)
- FAIL 1.0Inf is min( inf, nan)