Formal comment #8 (defect)
Unclear how equality predicates behave on NaN
Reported by: Per Bothner
Component: arithmetic
Version: 5.91
9.10.2 Numerical operations p 39
Arithmetic operations
= explicitly specifies the behavior for +inf.0 and -inf.0, but not for
+nan.0. Implicitly, if any operand is +nan.0, then the result should
be #f, and this follows IEEE-754. However, it should be stated
explicitly. (The same is also true for <, >, <= and >=.)
However, in that case the specification of eqv? (9.6 p 35) is
wrong. It says that (eqv? x y) implies that (= x y) for numbers. This
is wrong: (eqv? +nan.0 +nan.0) must return #t. This follows because we
really need (let ((x +nan.0)) (eq? x x)) to be #t.
If x and y are flonums of the same resolution/implementation, then a
reasonable definition of (eqv? x y) is that it is true if and only y
are bit-for-bit equal.
RESPONSE:
An example should be added to indicate that (= +nan.0 x) is false for
any x. Similar examples could be added for some of the other numerical
predicates.
Contrary to formal comment #8, the specification of eqv? does not say
that (eqv? x y) implies (= x y) when x and y are numbers. This should
be emphasized by an example.
Formal comment #8 gives no reason in support of its statement that
(let ((x +nan.0)) (eq? x x)) needs to be true. The R5RS does
not guarantee that (let ((x z)) (eq? x x)) will be true when
z is a number, and we see no reason for the R6RS to add such
a guarantee for the special case where z is a NaN.
It would have been more reasonable to argue that the R6RS
should require (let ((x E)) (eqv? x x)) to evaluate to true
when E evaluates to a number; both the R5RS and R6RS imply
this for certain other types, and for most numbers, but not
for NaNs. Since the IEEE-754 and draft IEEE-754R both say
that the interpretation of a NaN's payload is left up to
implementations, and implementations of Scheme often do not
have much control over the implementation of IEEE arithmetic,
it would be unwise for the R6RS to insist upon the truth of
(let ((x E)) (or (not (number? x)) (eqv? x x))), even though
that expression is likely to evaluate to true in most
systems. For example, a system with delayed boxing of
inexact real numbers may box the two arguments to eqv?
separately, the boxing process may involve a change of
precision, and the two separate changes of precision may
result in two different payloads.
When x and y are flonums represented in IEEE floating point or
similar, it is reasonable to implement (eqv? x y) by a bitwise
comparison of the floating point representations. The R6RS
should not require this, however, because (1) the R6RS does
not require that flonums be represented by a floating point
representation, (2) the interpretation of a NaN's payload
is explicitly implementation-dependent according to both the
IEEE-754 standard and the current draft of its proposed
replacement, IEEE-754R, and (3) the semantics of Scheme
should remain independent of bit-level representations.
For example, IEEE-754, IEEE-754R, and the draft R6RS all allow
the external representation +nan.0 to be read as a NaN whose
payload encodes the input port and position at which +nan.0
was read. This is no different from any other external
representation such as (), #(), or 324. An implementation
can have arbitrarily many bit-level representations of the
empty vector, for example, and some do. That is why the
behavior of the eq? and eqv? procedures on vectors cannot
be defined by reference to bit-level representations, and
must instead be defined explicitly.