Comment #3 on issue 3266 by impinball: Numeric issues in hyperbolic functions
http://code.google.com/p/v8/issues/detail?id=3266

I have a few algorithm patch suggestions (I can't test it because my copy currently doesn't compile):

Note: example implementations exclude type-checking, etc., and use ES6 syntax.

atanh(x) (division first reduces overflow:
log1p(x)/2 - log1p(-x)/2

Example implementation:
Math.atanh = x => log1p(x)/2 - log1p(-x)/2;

asinh(x) for large x:
|x|*sqrt(1 + (1/x)^2)

Example implementation:
Math.asinh = x => {
  // sign without branching or extra method call, good for arbitrary size
  let sign = (x > 0) - (x < 0);

  if (x < 0) x = -x; // simplistic absolute value

  // in last term of sqrt, take reciprocal in place
  return sign * Math.log(x * (1 + Math.sqrt(1 + (x=1/x) * x));
}

This one merits an explanation, so here it is:
Let's first look at the current implementation (~= means approximately equal to).

    asinh x ~= | ln(x + sqrt(x^2 + 1)),   x > 0
               | -ln(-x + sqrt(x^2 + 1)), x < 0

               (special cases excluded)

Now, let's toy around with the first one, with only positives:

    log(x + sqrt(x^2 + 1))


          /       |--------  \
          |       |  2       |
    = log | x + \ | x  + 1   |  <-- above ASCII-ified
          \      \|          /


          /           |------------- \
          |           |     / 1 \ 2  |
    = log | x + |x| \ | 1 + |---|    |
          \          \|     \ x /    /


          /         |------------- \
          |         |     / 1 \ 2  |
    = log | x + x \ | 1 + |---|    |  <-- |x| = x if x > 0
          \        \|     \ x /    /


Now, let's look at the second.

   -log(-x + sqrt(x^2 + 1))


           /        |--------  \
           |        |  2       |
    = -log | -x + \ | x  + 1   |  <-- above ASCII-ified
           \       \|          /


           /            |------------- \
           |            |     / 1 \ 2  |
    = -log | -x + |x| \ | 1 + |---|    |
           \           \|     \ x /    /


           /          |------------- \
           |          |     / 1 \ 2  |
    = -log | -x - x \ | 1 + |---|    |  <-- |x| = -x if x < 0
           \         \|     \ x /    /


If you notice, there are only two differences in the formula: the overall sign and the sign of the x in the first term, both negative if x is negative.

So, let l(x) be the following:

          |-------------
          |     / 1 \ 2
    1 + \ | 1 + |---|
         \|     \ x /


Now, we can redefine asinh x as:

    asinh x ~= | log (x + |x| l(x)),         x > 0
               | -log (-x + |x| l(x)), x < 0


And, now, let sign(x) be the sign of x. This lets us further abstract it as such, considering l is even and the first term is effectively |x|.

asinh x ~= sign(x) log |x| (1 + l(|x|)) <-- l(x) = l(-x), so l(x) = l(| x|)

    sign(x) = |  1 if x > 0
              | -1 if x < 0
              |  0 if x = 0

             |-------------
             |     / 1 \ 2
    l(x) = \ | 1 + |---|    <-- close to 0 will underflow to 1
            \|     \ x /


The part "log |x| (1 + l(x))" cannot be further improved without potential limit issues because these limits don't match up. to the proper limit of:

     lim    log |x| = -Infinity
    x -> 0

     lim   log (1 + l(x)) =  lim   log (1 + 1) = log 2
    x -> 0                  x -> 0

     lim   asinh x = 0
    x -> 0

This should explain my algorithm for asinh.

--
You received this message because this project is configured to send all issue notifications to this address.
You may adjust your notification preferences at:
https://code.google.com/hosting/settings

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to