| Marcin 'Qrczak' Kowalczyk 2004-09-22, 4:02 pm |
| Ray Dillinger <bear@sonic.net> writes:
>
> That is, um, interesting and unexpected. For one number to
> have two contradictory exactness values is very strange.
I agree that R5RS did not expect that. There are two reasons why I did
it that way:
1. My language Kogut implements complex numbers as pairs of real
numbers. It's dynamically typed, and complex numbers are "honestly"
implemented in it rather than using low-level C-defined type, so
it's natural to allow any real types as parts. Specialization of +
for complex numbers uses dispatched + for its parts.
If I was to force same exactness types as both parts (i.e. if an
integer or rational part was to be coerced to a float whenever the
other part is a float), it would bring two di vantages:
a) more work
b) less accurate results
for no real benefit.
2. I couldn't decide whether to provide a function which converts
two real numbers to a complex number, or to let programmers write
a+b*I. Finally I allowed both. But in order for a+b*I to yield
correct signs with -0.0, b*I must be distinguished from 0.0+b*I
(because x + 0.0 is not the same as x when x is -0.0).
C99 distinguishes them statically, with an imaginary type. William
Kahan in the article I mentioned previously tries to explain that
this distinction is useful for some numeric algorithms. It's hard
to read, because he complains a lot without telling what exactly
he complains about, technically, and what he would propose instead,
but I believe I got this fact right. He also says that a float
added to a complex should not be implemented by coercing the float
to a complex first, in order to not change the imaginary part if
it was -0.0, but Kogut already did that without problems.
So I considered introducing imaginary type, which would be
dynamically dispatched. Then I realized that if exact 0 was treated
appropriately in mixed arithmetic with floats (such that for
example x + 0 is x when x is a float), then a regular complex
number with exact 0 as its real part would serve as the imaginary
type, without having to implement its arithmetic specially.
> You will find that R5RS forbids very little. It is "compatible"
> with almost any correct (and, indeed, many incorrect) way of
> handling numbers.
This thread has been pointed out to me:
http://www.google.pl/groups?threadm...40aubrey.jaffer
It seems that R5RS is a bit incompatible with IEEE floating point
arithmetic. For example IEEE says that (= nan nan) is #f; Scheme
defines eqv? for numbers in terms of = and exactness; it says that eq
implies eqv; everybody expects (eq? x x) to be #t without checking
what x is. This is a contradiction of separate standards taken together.
I think the best way of breaking the contradiction would be to divorce
eqv? from =. The spirit of eqv? is that it tells whether two objects
are interchangeable when the amount of sharing of immutable objects is
not taken into account.
I think I will explicitly break R5RS in my implementation, as it's
already broken wrt. equality of nan. The R5RS definition of eqv? in
terms of = did not expect some corner cases: -0.0, nan, and complex
numbers with mixed exactness. Changing eqv? to conform to the idea
above fixes all these cases at once. I will do this:
(eqv? 0.0 -0.0) => #f
(eqv? nan nan) => #t
(eqv? 2+3.0i 2.0+3i) => #f
even though = is different:
(= 0.0 -0.0) => #t
(= nan nan) => #f
(= 2+3.0i 2.0+3i) => #t
and document it as a non-compliance with R5RS.
Printing representation of infinities and nan is a separate issue
discussed in that thread. I will tackle it later.
For anybody wanting to flame me: I did not invent -0.0 and nan, I only
want to support them in the way IEEE and many languages and hardware
processors do.
--
__("< Marcin Kowalczyk
\__/ qrczak@knm.org.pl
^^ http://qrnik.knm.org.pl/~qrczak/
|