# Re: My suggestions to the R6RS committee about numerics

This page is part of the web mail archives of SRFI 70 from before July 7th, 2015. The new archives for SRFI 70 contain all messages, not just those from before July 7th, 2015.

On Jun 2, 2005, at 12:25 PM, bear wrote:

On Wed, 18 May 2005, Bradley Lucier wrote:

The first part deals with IEEE 754/854 arithmetic.
If you don't support this arithmetic, then things are still up in the
air.

I should not like to see conforming implementations restricted
to exactly this representation and no other; It's nice to be
able to use real numbers of specified precision for some tasks
where the 500th bit of the mantissa actually matters.

I see I wrote this poorly. I mean that I am not able to imagine all possible inexact arithmetics, so I want to specify precisely how Scheme behaves on what I do know, which is IEEE 754 (and I suppose 854, but I don't know anyone who uses it).

6.1 Equivalence predicates

The eqv? procedure returns #t if:

*  obj1 and obj2 are both IEEE 754/854 format numbers, and obj1 and
obj2 have
the same base, sign, number of exponent digits, exponent bias, biased
exponent,
number of significand digits, and significand.

*  obj1 and obj2 are both exact numbers and are numerically equal
(see =, section 6.2).

The eqv? procedure returns #f if:

*  one of obj1 and obj2 is an exact number but the other is an
inexact number.

*  obj1 and obj2 are both IEEE 754/854 format numbers, and the base,
sign,
number of exponent digits, exponent bias, biased exponent, number of
significand
digits, or significand of obj1 and obj2 differ.

* obj1 and obj2 are exact numbers for which the = procedure returns #f.

I believe that it is also necessary to specify that if one or
more of the arguments is NaN, eqv? returns false.  It is not at
all uncommon for NaN to have a single bit pattern, and thus if
compared under the above definition, a call where two arguments
were both NaN would return #t, which is a mistake in arithmetic.

I disagree that this is a mistake in arithmetic. If two NaNs have the same bit pattern then they may are "substitutable", in that they should give the same result when put into any function. This is the case in Common Lisp, for example, where two NaNs with the same bit pattern are EQL.

It is not ideal IMO to declare it an error to pass NaN to eqv?,
since that limits the composition of expressions.

Note: This section does not state under which conditions eqv? returns
#t or #f
for inexact numbers that are not in IEEE 754/854 format.  We
recommend that
numbers not in IEEE 754/854 format for which a base, sign, number of
exponent
digits, exponent bias, biased exponent, number of significand digits,
and
significand can be defined follow the same rules as above.

I would require rather than recommend this property;

Unfortunately, this statement contradicts your previous one about eqv? returning #f if one of the arguments is a NaN.

what I'd
*recommend* is for you, in the future, to practice line
wrapping at an earlier column.  :-)

I'm not hard-wrapping lines at all; it appears that two different mail processors between me and you have a difference of opinion on how lines should be wrapped.

6.2.5. Numerical operations

(number? obj )                           procedure
(complex? obj )                          procedure
(real? obj )                             procedure
(rational? obj )                         procedure
(integer? obj )                          procedure

These numerical type predicates can be applied to any kind
of argument, including non-numbers. They return #t if the
object is of the named type, and otherwise they return #f.
In general, if a type predicate is true of a number then
all higher type predicates are also true of that number.
Consequently, if a type predicate is false of a number, then
all lower type predicates are also false of that number.

<delete this>
If z is an inexact complex number, then (real? z) is true
if and only if (zero? (imag-part z)) is true. If x is an
inexact real number, then (integer? x) is true if and only
if (= x (round x)).
If an implementation uses IEEE 754/854 format for inexact numbers then:
*  If z is an inexact complex number, then (real? z) is true if and
only if both (exact? (imag-part z)) and (zero? (imag-part z)) are true.
*  If z is an inexact real number, then (rational? z) is true if and
only
if z is not positive or negative infinity or a not-a-number.

Note:  IEEE 754/854 gives no way to represent complex numbers.

You will have to use more precise language to say what you mean,

How can I be more precise? I use "if and only if" and define real? in terms of exact?, zero?, and imag-part.

which is, I suspect, uses 754/854 to represent the real and
imaginary parts of the (cartesian) complex number, or uses
IEEE 754/854 to represent the magnitude and angle of the (polar)
complex number.

But once again, I don't think we need to specify this particular
format, nor to relax all restrictions if a different format is
used; In my opinion, the field widths for the mantissa and
biased exponent are irrelevant to the requirement.

I don't believe I'm specifying a format; exact?, zero? and imag-part are defined no matter what the internal format of complex numbers.

In particular, be careful not to assume that all complex numbers
are internally represented in the same way; several implementations,
I believe, use both polar and cartesian representations internally.

Finally, from a mathematical point of view, a number is either
inexact or exact; this includes complex numbers.  A few
implementations allow differing exactness in the real and
imaginary parts of a complex number.  This is a fact, but the
fact lies at some intermediate position between being a mere
artifact of the representation and a mathematical error.
Consult a number theorist to understand more fully the nature
of this error; it is beyond my feeble ability to explicate.

It is not sensible to speak of an inexact complex number whose
imaginary part is exact,

I believe that we should have

(exact? (imag-part 1.0)) => #t

and (complex? 1.0) is obviously true, and (inexact? 1.0) is true, so your statement makes no sense to me.

Furthermore,

(* 1.0 +1i)

exactly rotates 1.0 in the complex plane by 90 degrees, so one knows that its real part is exact 0.

and if you require an implementation
that allows it to be, there will be a certain amount of wailing
and gnashing of teeth from mathematicians and number theorists,
who are currently some of scheme's most dedicated and thankful
users.

I disagree. I'm a mathematician and I'm not wailing or gnashing my teeth.

(real? -2.5+0.0i) =) undetermined
(real? -2.5+0.0i) =) #f  if IEEE 754/854 arithmetic is used.

Nope, sorry, this just doesn't work.

I think it does.  You'll need more here than proof by proclamation.

For implementations that allow (real z) and (imag z) to have different
exactness, then (exact? z) returns #t if and only if both (exact?
(real z))
and (exact? (imag z)) return #t.

Once again, this proceeds from a delusory belief that one number,
as a result of its mere representation, can have two different
exactnesses simultaneously.  This will not fly.

If z is a complex number then precisely one of (exact? z) and (inexact? z) is true, not both, and not neither. A number does not have two different "exactnesses" simultaneously under my definition.

<Change the comparison predicates to the following>

(= z1 : : : ) procedure
(< x1 : : : ) procedure
(> x1 : : : ) procedure
(<= x1 : : : ) procedure
(>= x1 : : : ) procedure
<clip>
Note: If an implementation uses IEEE 754/854 format for its inexact
numbers, then these procedures shall return #f if called with two or
more arguments when one of the arguments is a NaN.

"one of" should be changed to "one or more of" -- this does
not change meaning, but adds clarity.  And I think this should
be true of NaN's always, no matter how numbers are represented.

The principle I'm working on here is that if

(proc x)

returns #t for any real finite or infinite x, then it should return #t for NaN. We have

(= x) => #t

for all real finite or infinite x, so this should also be true if x is NaN. Similarly for the other predicates.

<change the following predicates>

(zero? z) library procedure
(positive? x) library procedure
(negative? x) library procedure
(odd? n) library procedure
(even? n) library procedure

These numerical predicates test a number for a particular
property, returning #t or #f.

If an implementation uses IEEE 754/854 format for its inexact
numbers, then zero?, positive?, and negative? return #f if called with
a NaN argument.

I would go further and say *all* of these procedures should return
#f if called with a NaN argument, regardless of how numbers or NaNs
are represented.

NaNs are not integers, so they cannot be arguments to odd? and even?.

<change the following procedures>

(max x1 x2 : : : ) library procedure
(min x1 x2 : : : ) library procedure

These procedures return the maximum or minimum of their
arguments.

(max 3 4) =) 4 ; exact
(max 3.9 4) =) 4.0 ; inexact

If an implementation uses IEEE 754/854 format for its inexact
numbers, and any of the arguments to max and min are NaNs, then
max and min returns one of the NaN arguments as its result.

use "a" rather than "one of the", unless there is some
significance to the eq?-ness of the NaN returned with
one of its arguments.  And say simply "NaNs" if there is
not some significance to representing them in a particular
IEEE format.

Ah, so I should say "If the inexact format has NaNs then ..." rather then mentioning IEEE 754 explicitly? Or perhaps I will have to say "If the inexact format has NaNs that behave the same as NaNs in IEEE 754/854 arithmetic then ...", but the latter seems to be rather verbose.

<change floor, ceiling, truncate, and round to take rational arguments,
not real arguments>

I do not agree with you that these procedures ought not take real
arguments. Instead, they should return inexact numbers when given
inexact numbers and NaN when given NaN.

In my classification of numbers, the infinities and NaN cannot be integers, there is no "nearest integer" to these objects, either above or below them. Can you describe a use for

(trunc +inf.0) => ???

or something similar?

<change rationalize to take rational arguments, not real arguments>

This does not make sense.  If you have rational arguments to start
with, what are you using rationalize for?

To find a "simpler" rational approximation to the argument.

I agree, this needs to be fixed. I should have said that the infinities and NaNs are not valid arguments to rationalize. If a system wants to have numbers like (sqrt 2), then they should be allowed as arguments to rationalize, too.

• Follow-Ups: