Complex Issues

Sorry for the long silence here, it’s been a busy summer with far too little Perl 6. But I did squeeze in some work on trig, both on nom and niecza. And I ran into a very interesting issue.

My local copy of niecza has S32-trig/sin.t almost working. A few needed skips, but all the core numeric types work. Except…

> is_approx(asin(0.785398163404734 + 2i), 0.341338918259482 + 1.49709293866352i
# got:      2.80025373533031-1.49709293866352i
# expected: 0.341338918259482+1.49709293866352i

niecza> asin(0.785398163404734 + 2i)
2.80025373533031-1.49709293866352i

rakudo> asin(0.785398163404734 + 2i)
0.341338918259481 + 1.49709293866352i

Woah, what’s up with that? Well, it turns out both answers are right in some sense:

niecza> sin(asin(0.785398163404734 + 2i))
0.785398163404734+2i

rakudo> sin(asin(0.785398163404734 + 2i))
0.785398163404734 + 2i

The thing here is that sin is periodic; there are an infinite number of complex numbers it maps to the same result value. That means when you call asin, there are an infinite number of possible results for each input value, and you must somehow choose one of them.

But let’s take a step back from that and look at why I got different results, because I used the exact same formula for asin in both Rakudo and Niecza. That formula is -1i * log(($x)i + sqrt(1 - $x * $x)). Let’s look at the sqrt first:

niecza> my $x = 0.785398163404734 + 2i; sqrt(1 - $x * $x)
-2.21086930051619+0.710488099157523i

rakudo> my $x = 0.785398163404734 + 2i; sqrt(1 - $x * $x)
2.21086930051619 - 0.710488099157523i

As you can see, one answer is the negative of the other. Of course, when you square the results, that additional factor of -1 just goes away, so these are both valid results.

So this leads me to two questions:
1) Should we define one of these two answers as being correct, as far as Perl 6 is concerned? (Or should they both be considered valid results?)

2) If so, which one? And how do we coherently specify that branch?

I thought at first it might be as simple as saying “The branch where the complex result of sqrt for complex numbers with an imaginary value of 0 agrees with the real sqrt result.” But in fact both Rakudo and Niecza already seem to agree for the sqrts of real-valued Complex numbers.

Anyone else out there have a notion?

6 Responses to “Complex Issues”

  1. colomon Says:

    http://www.ida.liu.se/imported/cltl/clm/node129.html looks to have some interesting info…

  2. cjm Says:

    1) Yes, there should be one “correct” answer for all Perl 6 implementations.

    2) Let’s just follow Common Lisp. They’ve got the experience, and they’ve already written the definitions. Just make sure we get the current definitions. (I’m not a Lisp expert, so I’m not sure if the link you mention is up-to-date.)

    Here’s another link to look at:
    http://www.ai.mit.edu/projects/iiip/doc/CommonLISP/HyperSpec/Body/fun_asincm_acoscm_atan.html

  3. salva Says:

    follow C and POSIX (that AFAIK is similar to Common Lisp behavior):

    http://pubs.opengroup.org/onlinepubs/009695399/functions/csqrt.html

  4. sorear Says:

    The Perl 6 specs already specify “principal” square root, which apparently means the same thing that POSIX and Common Lisp specify. Niecza has now been fixed to do that too.

  5. jamarier Says:

    It’s a bad test because both answers are correct.

    (sqrt 1)^3 can be 1 or -1. and both are ok answers.

    Maybe a best test is something like ( asin(sin(asin x)) == asin x )

    • cjm Says:

      jamarier, I strongly disagree. You’re correct that in the mathematical sense, sqrt(x) has two answers, and asin(x) has an infinite number of answers.

      But in a computer programming language, the range of the sqrt and asin functions should be defined such that there is only 1 correct answer for each possible input. Programming is hard enough as it is; any surprises that can be removed should be removed.

Leave a reply to jamarier Cancel reply