Forum: Ruby-core [Bug #1715] Numeric#arg for NaN is Inconsistent Across Versions

Posted by Run Paint Run Run (Guest)
on 2009-07-02 17:27
(Received via mailing list)
Bug #1715: Numeric#arg for NaN is Inconsistent Across Versions
http://redmine.ruby-lang.org/issues/show/1715

Author: Run Paint Run Run
Status: Open, Priority: Low
ruby -v: ruby 1.9.2dev (2009-07-01 trunk 23924) [i686-linux]

Numeric#arg gives 0 for NaN on 1.9; Pi for NaN on 1.8:

   $ ruby -v -rcomplex -e 'p (0/0.0).arg'
   ruby 1.9.2dev (2009-07-01 trunk 23924) [i686-linux]
   -e:1: warning: (...) interpreted as grouped expression
   0

   $ ruby -v -e 'p (0/0.0).arg'
   ruby 1.9.2dev (2009-07-01 trunk 23924) [i686-linux]
   -e:1: warning: (...) interpreted as grouped expression
   0

   $ ruby8 -v -rcomplex -e 'p (0/0.0).arg'
   ruby 1.8.8dev (2009-07-01) [i686-linux]
   -e:1: warning: (...) interpreted as grouped expression
   3.14159265358979
Posted by Run Paint Run Run (Guest)
on 2009-07-02 18:47
(Received via mailing list)
Issue #1715 has been updated by Run Paint Run Run.


The same inconsistency can be seen with #polar:

    $ ruby -rcomplex -e 'p (0/0.00).polar'
    [NaN, 0]

    $ ruby86 -rcomplex -e 'p (0/0.00).polar'
    [NaN, 3.14159265358979]

----------------------------------------
http://redmine.ruby-lang.org/issues/show/1715
Posted by Yukihiro Matsumoto (Guest)
on 2009-07-03 02:25
(Received via mailing list)
Hi,

In message "Re: [ruby-core:24116] [Bug #1715] Numeric#arg for NaN is 
Inconsistent Across Versions"
    on Fri, 3 Jul 2009 00:26:44 +0900, Run Paint Run Run 
<redmine@ruby-lang.org> writes:

|Numeric#arg gives 0 for NaN on 1.9; Pi for NaN on 1.8:

Both versions should return NaN for NaN.

              matz.
Posted by Matthias Wächter (Guest)
on 2009-07-04 21:40
(Received via mailing list)
Yukihiro Matsumoto schrieb:
> In message "Re: [ruby-core:24116] [Bug #1715] Numeric#arg for NaN is Inconsistent Across Versions"
>     on Fri, 3 Jul 2009 00:26:44 +0900, Run Paint Run Run <redmine@ruby-lang.org> writes:
> 
> |Numeric#arg gives 0 for NaN on 1.9; Pi for NaN on 1.8:
> 
> Both versions should return NaN for NaN.

If we want to be precise in the Unknown, maybe both versions should give
NaN for 0.arg and 0.0.arg (and Complex zero) as well?

– Matthias
Posted by Matthias Wächter (Guest)
on 2009-07-04 22:08
(Received via mailing list)
Matthias Wächter wrote:
> If we want to be precise in the Unknown, maybe both versions should give 
> NaN for 0.arg and 0.0.arg (and Complex zero) as well?

and, while we are at it:

require 'complex'
p Complex.polar(0,   0/0.0)  # => expected: Complex(0,0)
p Complex.polar(0.0, 0/0.0)  # => expected: Complex(0.0,0.0)


but returns:
Complex(NaN,NaN)
Complex(NaN,NaN)

– Matthias
Posted by tadayoshi funaba (Guest)
on 2009-07-05 12:29
(Received via mailing list)
Issue #1715 has been updated by tadayoshi funaba.


>> If we want to be precise in the Unknown, maybe both versions should give 
>> NaN for 0.arg and 0.0.arg (and Complex zero) as well?

no.

> but returns:
> Complex(NaN,NaN)

i hope not.

complex try to preserve flonum status.

Complex(-0.0, 0.0).polar #=> [0.0, 3.141592653589793]
Complex(0.0, -0.0).polar #=> [0.0, -0.0]
Complex(-0.0, -0.0).polar #=> [0.0, -3.141592653589793]

Complex.polar(0.0, 3.141592653589793) #=> (-0.0+0.0i)
Complex.polar(0.0, -0.0) #=> (0.0-0.0i)
Complex.polar(0.0, -3.141592653589793) #=> (-0.0-0.0i)

anyway, complex respects flonum's rules.
----------------------------------------
http://redmine.ruby-lang.org/issues/show/1715
Posted by Matthias Wächter (Guest)
on 2009-07-07 21:08
(Received via mailing list)
On 7/5/2009 12:28 PM, tadayoshi funaba wrote:
>>> If we want to be precise in the Unknown, maybe both versions should give 
>>> NaN for 0.arg and 0.0.arg (and Complex zero) as well?
> 
> no.
> 
>> but returns:
>> Complex(NaN,NaN)
> 
> i hope not.

What is it that you don’t hope? That Complex.polar(0.0,0/0.0) returns 
Complex(NaN,NaN), or that I think that this is wrong?


> complex try to preserve flonum status.

Which is completely nonsense. Since there is no _correct_ positive 
flonum status for +0.0 (as it is mixed with precise 0), there is really 
little use
in -0.0, and it should not be used as an entry point for further 
calculations. Zero is zero and never the approximation, even if floats 
allow that as
a result of a calculation to give a hint in very special situations.

> Complex(-0.0, 0.0).polar #=> [0.0, 3.141592653589793]
> Complex(0.0, -0.0).polar #=> [0.0, -0.0]
> Complex(-0.0, -0.0).polar #=> [0.0, -3.141592653589793]

That’s completely senseless: If you say Complex(-0.0, -0.0), you 
approach zero very explicitly from -135 degrees, why should it be -180 
degrees then?
Assuming approaching behavior for -0.0 and 0.0, all argument values 
would have to be something like PI/4 +- n*PI/2, so zero-length arrows in 
+-45
degrees and +-135 degrees then.

> Complex.polar(0.0, 3.141592653589793) #=> (-0.0+0.0i)
> Complex.polar(0.0, -0.0) #=> (0.0-0.0i)
> Complex.polar(0.0, -3.141592653589793) #=> (-0.0-0.0i)

The problem with this useless precision in the approaching range around 
zero is that you _have_ to make Complex.polar(0.0, 0/0.0) equal to
Complex(NaN, NaN), just because you cannot decide between -0.0 and +0.0, 
especially their various combinations in the complex layer! Really 
senseless,
sorry, it should be zero as it is, and zero is 0.0 (vice versa).

Just to repeat myself: The difference between +0 and -0 is very esoteric 
and incompletely implemented in IEEE 754, as there is no explicit 
precise 0
if you want to use 0.0 as +0.0 and -0.0 as its negative. The use of -0 
is not advised in general calculation but in very special situations,
especially hard to apply to the complex numbers. In fact, whenever 0.0 
is used, instead of assuming it to mean +0.0, it is precisely zero, 
except for
the cases specified in IEEE 754 to get positive or negative Infinity. 
Clearly, the argument of 0 is NaN, and polar should equally know how to 
handle
NaN arguments for a 0.0 distance.

Similarly, if you invert 0.0, you should get NaN because you cannot 
decide between +Infinity or -Infinity. In addition to the missing 
precise zero (Q:
is it missing at all?), IEEE 754 should define a unknown Infinity, 
something that could be either positive or negative.

> anyway, complex respects flonum's rules.

For no practical reason except underflow and infinity handling which are 
special cases anyway.

Other examples where standard formulae don’t help much:

a=Complex(1/0.0,3)
b=Complex(1/0.0,-5)

p a*a      # => Complex(Infinity, Infinity), expected: Complex(Infinity, 
0.0)
p a*b      # => Complex(Infinity, NaN), expected: Complex(Infinity, 0.0)
p a/b      # => Complex(NaN, NaN), expected: Complex(NaN, 0.0)

here, you will see that the imaginary part approaches zero quicker than 
the real part, so while the latter two examples are at least covered in 
the
result given by ruby, the first is clearly wrong.

– Matthias
Posted by tadayoshi funaba (Guest)
on 2009-07-08 15:06
(Received via mailing list)
Issue #1715 has been updated by tadayoshi funaba.


i'm nothing special.
so far, my policy (maybe entirely ruby too, i believe) is
don't bother calculation of flonum.
that's all.
basically, the result depends machine's representation and libm (atan2 
etc).

i don't know detail, but kahan's proposal was accepted widely.
see about phase of cltl2 and angle of r6rs.
see also carg(3) and atan2(3).

zero is zero, right, but sign is preserved.

1.0/Inf #=> 0.0
(-1.0)/Inf #=> -0.0

Complex(0.0,1.0)/Inf #=> (0.0+0.0i)
Complex(0.0,-1.0)/Inf #=> (0.0-0.0i)

in ruby,
if both NaN * 0 and NaN * 0.0 return zero
and don't distinguish -0.0 and +0.0,
you have a chance a little, i think.

so far, i don't think Complex should wipe the given NaN out.
i don't think 0.arg should return NaN.

i'm going to follow common sense in this world, not nonsense.

----------------------------------------
http://redmine.ruby-lang.org/issues/show/1715
Posted by tadayoshi funaba (Guest)
on 2009-07-08 16:34
(Received via mailing list)
Issue #1715 has been updated by tadayoshi funaba.


you wrote:
> p a*a      # => Complex(Infinity, Infinity), expected: Complex(Infinity, 0.0)

i tested some implementations.

                gosh: +inf.0+inf.0i
               guile: +inf.0+inf.0i
             larceny: +inf.0+inf.0i
            mzscheme: +inf.0+inf.0i
             ypsilon: +inf.0+inf.0i
              gambit: +inf.0+inf.0i
                 gcc: +inf+inf
             python3: (inf+infj)
               perl5: inf+infi
                 ghc: Infinity :+ Infinity
                hugs: inf :+ inf
              octave: Inf + Infi
              squeak: Infinity + Infinity

your opinion seems to be very very minority.

and i've never seen arg(0) return NaN.

----------------------------------------
http://redmine.ruby-lang.org/issues/show/1715
Posted by Matthias Wächter (Guest)
on 2009-07-08 23:04
(Received via mailing list)
tadayoshi funaba wrote:
>               gambit: +inf.0+inf.0i
>                  gcc: +inf+inf
>              python3: (inf+infj)
>                perl5: inf+infi
>                  ghc: Infinity :+ Infinity
>                 hugs: inf :+ inf
>               octave: Inf + Infi
>               squeak: Infinity + Infinity
> 
> your opinion seems to be very very minority.

You are right, my fault. while it _is_ Complex(Infinity, Infinity), it
would have an arg of 0.0, though. :-)

> and i've never seen arg(0) return NaN.

That’s pure math. I think folks hate to see NaNs (and the associated
traps or exceptions in some languages) in trivial examples like that,
but NaN would be correct. Still, most people set it to some reasonable
value like 0.0 by convention, but not by math.

In any case, Complex.polar(0.0, NaN) must return Complex(0.0, 0.0)
instead of Complex(NaN, NaN).

Thanks,
– Matthias
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.