Fast hyperbolic tangent approximation in Javascript
I'm doing some digital signal proce开发者_高级运维ssing calculations in javascript, and I found that calculating the hyperbolic tangent (tanh) is a bit too expensive. This is how I currently approximate tanh:
function tanh (arg) {
// sinh(number)/cosh(number)
return (Math.exp(arg) - Math.exp(-arg)) / (Math.exp(arg) + Math.exp(-arg));
}
Anyone knows a faster way to calculate it?
From here.
function rational_tanh(x)
{
if( x < -3 )
return -1;
else if( x > 3 )
return 1;
else
return x * ( 27 + x * x ) / ( 27 + 9 * x * x );
}
This is a rational function to approximate a tanh-like soft clipper. It is based on the pade-approximation of the tanh function with tweaked coefficients.
The function is in the range x=-3..3 and outputs the range y=-1..1. Beyond this range the output must be clamped to -1..1.
The first to derivatives of the function vanish at -3 and 3, so the transition to the hard clipped region is C2-continuous.
The Padé approximation is magnitudes better than the Taylor expansion. The clamping may also be an issue (depending on your range).
You could do this and cut your performance time in half:
function tanh(arg) {
var pos = Math.exp(arg);
var neg = Math.exp(-arg);
return (pos - neg) / (pos + neg);
}
Not sure of how big the performance increase will be, but
(exp(x) - exp(-x))/(exp(x) + exp(-x)) = (exp(2x) - 1)/(exp(2x) + 1)
You'll cut the number of exp
s in half.
For an accurate answer using fewer Math.exp()
s, you can use the relationship between tanh and the logistic function. Tanh(x)
is exactly 2 * logistic(2 * x) - 1
, and expanding out the logistic function, you get:
function one_exp_tanh(x){
return 2.0 / (1.0 + exp(-2.0 * x)) - 1.0;
}
I don't know whether that is faster in javascript though.
ES6 provides this method and many other trig functions natively:
Math.sinh
– hyperbolic sine of a numberMath.cosh
– hyperbolic cosine of a numberMath.tanh
– hyperbolic tangent of a numberMath.asinh
– hyperbolic arc-sine of a numberMath.acosh
– hyperbolic arc-cosine of a numberMath.atanh
– hyperbolic arc-tangent of a numberMath.hypot
– square root of the sum of squares
most probably it would be faster than most of JS alternatives.
You could always cut the formula off at a certain number level of accuracy.
function tanh (x) {
return arg - (x * x * x / 3) + (2 * x * x * x * x * x / 15);
}
this is my answer to this problem
function tanh(x){
var e = Math.exp(2*x)
return (e-1)/(e+1)
}
Math.constructor.prototype.tanh=tanh;
document.write(Math.tanh(2))
Calling that function on chrome takes less than three times of what it takes to call an empty function f(){}
so I think that you are not going to gain much with any rewriting.
The problem is the function overhead, not the formula. May be inlining it could save something more interesting...
EDIT
To make the test what I did was just opening a console in Chrome (ctrl-shift-C) and created a timing function with
timeit = function(f) {
var start=(new Date).getTime();
for (var i=0; i<100000; i++)
f(1);
return (new Date).getTime() - start;
}
and then tested it with function(){}
and with your function.
It turns out however that this kind of test is very unreliable. I even got absurd results with timeit(f1)
reporting 200 and timeit(f2)
reporting 120 (quite a difference) but f1
and f2
were indeed two variables linked to the same function object. Also there was a difference between timeit(f)
and timeit(function(x){ return Math.cos(x); })
even when f
was exactly that function.
May be there is an explanation because of how V8 and the javascript console interact but I don't know what it is.
Also with FF4 this approach gives very unreliable results...
精彩评论