hyln9 / ikarus

Optimizing incremental native-code compiler for R6RS scheme. This is a forked repository.
https://launchpad.net/ikarus
Other
5 stars 0 forks source link

comparing nan with nan #239

Closed hyln9 closed 10 years ago

hyln9 commented 10 years ago

The following:

(import (rnrs)) (write (list (equal? +nan.0 +nan.0) (equal? +nan.0 -nan.0) (equal? -nan.0 +nan.0) (equal? +nan.0 (string->number "+nan.0")) (equal? +nan.0 (string->number "-nan.0")) (equal? -nan.0 (string->number "+nan.0")) (equal? -nan.0 (string->number "-nan.0")) ))

under Ikarus revision 1827 prints: (#t #t #t #f #f #f #f) IMHO it should print all #t. Larceny and Ypsilon print all #t. Mosh prints (#t #f #f #t #f #f #f), which I also reported as bug.

Launchpad Details: #LP401114 Marco Maggi - 2009-07-18 11:17:11 -0400

hyln9 commented 10 years ago

On Jul 18, 2009, at 6:17 PM, Marco Maggi wrote:

IMHO it should print all #t.

What does the spec say?

Aziz,,,

Launchpad Details: #LPC Abdulaziz Ghuloum - 2009-07-18 11:42:50 -0400

hyln9 commented 10 years ago

On Jul 18, 2009, at 6:17 PM, Marco Maggi wrote:

Larceny and Ypsilon print all #t

My version of larceny (v0.962) prints all #f.

In short, the spec does not specify the results of this situation IIRC. I can dig it up if you can't find it.

Aziz,,,

Launchpad Details: #LPC Abdulaziz Ghuloum - 2009-07-18 11:47:35 -0400

hyln9 commented 10 years ago

The spec says that the result of EQV? is unspecified when applied to nan and nan. But I do not see why it should be false. What I know is that when I run some test that returns nan (plus or minus, does not matter) sometimes it fails, like this:

(check (string->number "+nan.0") => +nan.0)

but then the following is successful:

(check '(+nan.0) => '(+nan.0))

I see an inconsistency here, even if it is R6RS compliant. I discovered this while testing the SILex lexer; why should the tests of the lexer fail like the first case above?

I see that Ikarus makes +nan.0 and -nan.0 EQ? to themselves and to each other when they are read directly. But:

(eq? +nan.0 (string->number "+nan.0"))

is #f. Why STRING->NUMBER does not just return the unique nan value?

Launchpad Details: #LPC Marco Maggi - 2009-07-18 16:08:25 -0400

hyln9 commented 10 years ago

On Jul 18, 2009, at 11:08 PM, Marco Maggi wrote:

The spec says that the result of EQV? is unspecified when applied to nan and nan.

Correct.

But I do not see why it should be false.

I'm not saying it should be false (or true). You see, eqv? does both pointer comparison and value comparison. It happens that some +nan.0 values have the same address, and in that case, eqv? returns #t. It also happens that two +nan.0s do NOT have the same arithmetic value e.g., (= +nan.0 +nan.0) => #f. This is why eqv? returns #t sometimes and #f at other times.

What I know is that when I run some test that returns nan (plus or minus, does not matter) sometimes it fails, like this:

[aside: Nans do not have a sign: they're neither positive nor negative. I've fallen for this once when I was implementing number->string. Scheme's syntax is a little confusing in this regard.]

(check (string->number "+nan.0") => +nan.0)

but then the following is successful:

(check '(+nan.0) => '(+nan.0))

I see an inconsistency here, even if it is R6RS compliant.

Unspecified things don't need to be consistent. That's why they are left unspecified. :-) This is just like comparing numbers using eq?: it sometimes works and sometimes doesn't, yielding inconsistent results.

The bottom line is that you cannot depend on this even if Ikarus fixes on one behavior or another (and I don't see one of them as obviously the right way of doing things, and that's why I'd rather not fix one specific behavior).

I discovered this while testing the SILex lexer; why should the tests of the lexer fail like the first case above?

I can make that pass, but then other things would still fail, like (check (fl+ +nan.0 1.0) +nan.0) or (check (fl/ +inf.0 +inf.0) +nan.0)

The best way to test for nan-ness is by using the "nan?" or "flnan?" procedure.

I see that Ikarus makes +nan.0 and -nan.0 EQ? to themselves and to each other when they are read directly.

This is not a guaranteed behavior, so, you cannot depend on it. (I didn't intend for this to happen. This is an artifact of how the current compiler and the assembler work. If the compiler behaved differently, you'd see different behavior, even if the code for reading +nan.0 does not change.)

But:

(eq? +nan.0 (string->number "+nan.0"))

is #f.

This is not guaranteed or intended either.

Why STRING->NUMBER does not just return the unique nan value?

Because there is no one unique +nan.0 value just like how there is no one unique empty string or empty vector value. Guaranteeing that there is exactly one such object at any given point is too expensive. (Every computation that could produce a nan should be checked so that the-one-nan is returned instead of the actual result. This would simply kill the performance of most floating point procedures.)

Aziz,,,

Launchpad Details: #LPC Abdulaziz Ghuloum - 2009-07-18 18:46:28 -0400

hyln9 commented 10 years ago

Thanks for the useful information.

Why STRING->NUMBER does not just return the unique nan value?

Because there is no one unique +nan.0 value

I used bad word here, I meant the "special" one that is used while reading the code. But I see why it would not work.

Anyway, nan different from nan is not the result of least surprise. I do not think that someone expects eqv? and equal? to be fast, so an additional test that changes the return value to #t when both the operands are nan is, IMHO, acceptable. And it would be still R6RS compliant. In the end, nan is not-a-number, not not-a-value; and a value that is not equal to itself is weird.

With the current implementation it is like "+nan.0" and "-nan.0" represent some class of values, each different from the other. I see no usefulness in making this visible through the return values of eqv? and equal?. Unless there is a strong penalty in the efficiency of the implementation of equal? and eqv?.

Launchpad Details: #LPC Marco Maggi - 2009-07-19 14:59:42 -0400

hyln9 commented 10 years ago

Fixed in revision 1828 and added appropriate tests so that we don't regress.

Launchpad Details: #LPC Abdulaziz Ghuloum - 2009-07-20 03:07:34 -0400