Fwd: Proposal to integrate IEEE 754/854 and RnRS Bradley Lucier (20 Jul 2026 21:29 UTC)
Relaxing some error requirements Sergei Egorov (21 Jul 2026 03:24 UTC)

Fwd: Proposal to integrate IEEE 754/854 and RnRS Bradley Lucier 20 Jul 2026 21:28 UTC

Just for the record, this was my 1998 proposal about how to handle
arithmetic in Scheme.

-------- Forwarded Message --------
Subject: Proposal to integrate IEEE 754/854 and RnRS
Date: Mon, 3 Aug 1998 12:56:17 -0500
From: Brad Lucier <xxxxxx@math.purdue.edu>
To: xxxxxx@***.com
CC: xxxxxx@math.purdue.edu, xxxxxx@***.ca, xxxxxx@***.edu

I would like to propose the following changes to RnRS to integrate
IEEE 754/854 Arithmetic Standards into RnRS.  I'm sending this to
xxxxxx@***.edu since mzscheme makes different choices about what
to do with infinities, Not-a-Numbers, etc., and I would be interested
to hear about the rationale behind their choices and whether we can
come up with a standard proposal.

These proposals have been integrated into the next version of Gambit-C
(I think---I have discussed them with Feeley and sent along an
implementation).

Brad Lucier     xxxxxx@math.purdue.edu

Proposal for integrating IEEE Floating-Point Arithmetic Standards 754
and 854 with RnRS.

I've gone over all of the back issues of the rrrs-authors mailing list
(OK, I haven't read everything about exceptions and modules, but ...),
and I would like to make certain proposals for Scheme arithmetic that I
believe would bring it more in line with IEEE 754 and 854 arithmetic,
*if* this arithmetic is used to implements (some) inexact numbers in
a Scheme system.  If other inexact number systems are used, then my
suggestions would have no force.

Note that the IEEE arithmetic standards say nothing about mixing integer
(or exact) arithmetic with floating-point (or inexact) arithmetic; many of
my suggestions below try to Do-the-Right-Thing (TM) in these situations.

General Principles:

1.  If an expression contains Not-a-Numbers (NaNs), and the same result
would be obtained for any substitution of those NaNs by finite or infinite
numbers, then that result should be returned.

2.  I'm motivated by the following ansatz, taken from the
sequence+ultrafilter construction of the non-standard reals.  On input,
+0. represents all sequences of positive numbers tending to zero;
-0. represents all sequences of negative numbers tending to zero;
+inf. represents all sequences of numbers tending to positive infinity;
-inf. represents all sequences of numbers tending to negative infinity;
and NaN represents all sequences of numbers.  Results of operations
between these quantities and exact numbers should be motivated by applying
the same operation to all numbers of the sequence, e.g.,

(* 0 NaN) => the sequence (* 0 x1) (* 0 x2) (* 0 x3) .... for any (x1,
x2, ...)
           => the sequence 0 0 0 0 0 0 0 ...  => exact 0

3.  As a consequence of (2), +0. cannot be considered to lie exactly on
zero, it lies a little to the right of zero on the number line; similarly,
-0. lies a little to the left of zero.

Recommendations:

1. Infinities:

I suggest that Scheme adopt standard read and write forms for IEEE
positive and negative infinity, e.g., +inf. -inf.

2. Not-a-Numbers:

I suggest that Scheme adopt standard write forms for IEEE NaNs that
at least indicate the sign of the NaN, e.g., +nan. -nan.

3. eqv?:

If x and y are two inexact numbers implemented as IEEE floating point
numbers, then

(eqv? x y) => #t

if and only if x and y have the same base, sign, number of exponent
digits, biased exponents, number of significand digits, and significands.

Rationale:  No matter what bit-level representation is used for IEEE
floating-point numbers (and Motorola, Intel, and others use several,
beyond the issue of byte ordering), the quantities I specified above
are well-defined by IEEE 754 and 854.

The current definition of eqv? does not distinguish between -0. and +0.
(since
they are both equal (in the sense of Scheme =) and inexact.  However,
operations on +0. and -0. can yield very different results, e.g.,

(/ 1. -0) => -inf.
(/ 1. +0.) => +inf.

Also, since

(= x x) => #f

if x is a NaN, the current definition would say that any Nan is not
eqv to itself, which doesn't make much sense.

4. Non-coercion of exact 0:

If x is any number, and 0 represents exact zero, then

(+ x 0) => x
(- x 0) => x
(- 0 x) => (- x)
(* x 0) => 0
(/ 0 x) => 0
(/ x 0) => error

Rationale:  These results follow from a generalization of Priciple 1 that
goes as follows:  If an expression contains inexact numbers, and the
result of the expression does not change no matter what exact numbers
are substituted for the inexact numbers, then that result should be
returned.  There are other reasons for wanting to do this; the biggest
practical reason is that it makes operations between reals and complexes,
where the real x is coerced to (make-rectangular x 0), *much* easier
to get correct.  Here are some other arguments.

IEEE arithmetic has two zeros associated with the two affine
infinities.  Thus, there is no clear a priori choice of which inexact
0 (+0. or -0.) exact zero should be coerced to in these expressions.

IBM and Motorola indicate in their notes on the fused-multiply-add
instruction

a*b+c => d

that if one wants to only multiply two numbers, then one needs to set
d to -0., since if a*b is -0.,

-0. + +0. => +0.

will give the wrong result.  In a similar way, the exact zero in

(+ x 0)

should be coerced to -0., not +0., when x is an IEEE floating-point
number.  Similar analyses can be done with the other expressions given
above.

These rules simplify many basic arithmetic computations on complex numbers
with IEEE-conforming real and imaginary parts.

5.  Extending the order operators to 0 or 1 arguments.

For any numbers x

(< x) => #t
(= x) => #t
etc.

Also

(<) => #t
etc.

Rationale:  These operators are defined as asking whether a sequence
satisfies a certain order relationship.  These order relationships are
automatically satisfied by sequences of length 0 or 1.  This would simplify
(apply op lst) if lst is a list of 0 or 1 elements.

Note that even though

(op NaN x) => #f

for any order op, if

(op x) => #t

for any finite or infinite x (which I believe should be true), then

(op NaN) => #t
by General Principle 1.

6.  Transcendental functions with special exact arguments

I think it's time to require

(log 1) => 0
(exp 0) => 1
(sin 0) => 0
(cos 0) => 1
(tan 0) => 0
(atan 0) => 0
(asin 0) => 0

etc.

7.  Complex numbers with imaginary parts +0. or -0.

If IEEE arithmetic had only one zero, then I might be willing to have, e.g.,

(real? 1+0.i) => #t

However, it has two zeros, and I believe that those zeros should be
considered
as lying either slightly above or slightly below the real axis (as the
specifications of the branch cuts of many transcendental functions already
consider them), so if the imaginary part of a number is represented using
IEEE arithmetic, I suggest

(real? 1.+0.i) => #f
(real? 1.-0.i) => #f

but still

(real? 1.+0i) => #t

If 1.+0.i is not real, it is not rational, integer, etc.

I realize that a similar argument could be made that Scheme should have

(integer? +0.) => #f
(integer? -0.) => #f

but I'm not sure that would be acceptable.

8.  sqrt and log

If an implementation does not support complex numbers, and x is less
than zero then

(sqrt x) => nan.

as is required by IEEE 754; note that IEEE 754 also requires

(sqrt -0.) => -0.

and

(sqrt nan.) => nan.

I suggest that if x is not a NaN, and the sign of x is negative, then

(log x) => nan.

(log +0.) => -inf.

(log nan.) => nan.

without noting the sign of the NaNs.  The first two requirements could
follow from my General Principle 2.

If an implementation does support complex numbers, then an argument
could be made that

(sqrt nan) => nan.+nan.i

following principle 2; similarly for log.  However, I do not suggest
that the results of (sqrt nan) or (log nan) depend on whether complex
numbers are implemented.

9. magnitude

If z is a complex number, and one of the components of z is positive or
negative infinity, then

(magnitude z) => +inf.

whether or not the other component is a NaN.  This is from Principle 1
(and is given as an example in some of Kahan's lecture notes).

10. max and min

There are two ways to deal with NaN arguments in max and min---either
return one of the NaN arguments, or treat NaN arguments as missing values.
The second choice is what Kahan suggests and what Apple implements.

I suggest we pick one of these ways of dealing with NaNs so that programs
can be portable; I don't care which way, but I generally don't like
differing with Kahan without a reason.

11. rational?

If arg is +inf., -inf., or a NaN, then

(real? arg) => #t

but

(rational? arg) => #f

Rationale:  while the usual operations of arithmetic are defined in IEEE
754 and 854 as well for +inf., -inf., and NaNs as they are for other
floating-point numbers, there are some Scheme operations that do not
make sense with these arguments---numerator, denominator, round, etc.
(See below.)  rational? should be a predicate that checks whether these
operators will return a reasonable result.

12. truncate, round, ceiling, floor

If op is one of truncate, round, ceiling, floor, and numerator, then

(op -0.) => -0.

and

(denominator -0.) => 1.

If op is any of the previous operators, then

(rational? arg) => #f

implies

(op arg) => error

Rationale: These operations should preserve the sign of zero, since -0.
is as much an integer as +0.

The listed ops do not make sense for objects that are not rational?.

13. rationalize

If x is not rational? (see above) or y is not greater or equal to zero

(rationalize x y) => error

If y is +inf. and x is inexact rational then

(rationalize x y) => 0.

or if x is exact rational then

(rationalize x y) => 0

13. atan, asin, acos, log, etc.

The complex functions with branch cuts should be changed to conform
to the following pages in the CL Hyperspec:

http://www.harlequin.com/education/books/HyperSpec/Body/fun_asincm_acoscm_atan.html

http://www.harlequin.com/education/books/HyperSpec/Issues/iss069-writeup.html

These changes conform to the interpretation given above of +0. and
-0. lying to the right and to the left of exact zero.