Skip to content

Commit

Permalink
[Fix] Math.{asinh,atanh,cosh,sinh} precision (from #338)
Browse files Browse the repository at this point in the history
  • Loading branch information
ljharb committed Apr 6, 2017
1 parent 058a0ac commit ec76d49
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 19 deletions.
58 changes: 39 additions & 19 deletions es6-shim.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,14 @@
if (numberIsNaN(number)) { return number; }
return number < 0 ? -1 : 1;
};
var _log1p = function log1p(value) {
var x = Number(value);
if (x < -1 || numberIsNaN(x)) { return NaN; }
if (x === 0 || x === Infinity) { return x; }
if (x === -1) { return -Infinity; }

return (1 + x) - 1 === 0 ? x : x * (_log(1 + x) / ((1 + x) - 1));
};

// taken directly from https://github.com/ljharb/is-arguments/blob/master/index.js
// can be replaced with require('is-arguments') if we ever use a build process instead
Expand Down Expand Up @@ -1886,18 +1894,26 @@
if (x === 0 || !globalIsFinite(x)) {
return x;
}
return x < 0 ? -asinh(-x) : _log(x + _sqrt((x * x) + 1));

var a = _abs(x);
if (a < 1) {
return _sign(x) * _log1p(a + a * a / (_sqrt(a * a + 1) + 1));
}
return _sign(x) * (_log1p(a / 2 + _sqrt(1 + 1 / (a * a)) * a / 2 - 1) + 1 / LOG2E);
},

atanh: function atanh(value) {
var x = Number(value);

if (x === 0) { return x; }
if (x === -1) { return -Infinity; }
if (x === 1) { return Infinity; }
if (numberIsNaN(x) || x < -1 || x > 1) {
return NaN;
}
if (x === -1) { return -Infinity; }
if (x === 1) { return Infinity; }
if (x === 0) { return x; }
return 0.5 * _log((1 + x) / (1 - x));

var a = _abs(x);
return _sign(x) * _log1p(2 * a / (1 - a)) / 2;
},

cbrt: function cbrt(value) {
Expand Down Expand Up @@ -1931,9 +1947,9 @@
if (x === 0) { return 1; } // +0 or -0
if (numberIsNaN(x)) { return NaN; }
if (!globalIsFinite(x)) { return Infinity; }
if (x < 0) { x = -x; }
if (x > 21) { return _exp(x) / 2; }
return (_exp(x) + _exp(-x)) / 2;

var t = _exp(_abs(x) - 1);
return (t + 1 / (t * E * E)) * (E / 2);
},

expm1: function expm1(value) {
Expand Down Expand Up @@ -1980,25 +1996,21 @@
return _log(value) * LOG10E;
},

log1p: function log1p(value) {
var x = Number(value);
if (x < -1 || numberIsNaN(x)) { return NaN; }
if (x === 0 || x === Infinity) { return x; }
if (x === -1) { return -Infinity; }

return (1 + x) - 1 === 0 ? x : x * (_log(1 + x) / ((1 + x) - 1));
},
log1p: _log1p,

sign: _sign,

sinh: function sinh(value) {
var x = Number(value);
if (!globalIsFinite(x) || x === 0) { return x; }

if (_abs(x) < 1) {
return (Math.expm1(x) - Math.expm1(-x)) / 2;
var a = _abs(x);
if (a < 1) {
var u = Math.expm1(a);
return _sign(x) * u * (1 + (1 / (u + 1))) / 2;
}
return (_exp(x - 1) - _exp(-x - 1)) * E / 2;
var t = _exp(a - 1);
return _sign(x) * (t - (1 / (t * E * E))) * (E / 2);
},

tanh: function tanh(value) {
Expand Down Expand Up @@ -2051,10 +2063,18 @@
}
};
defineProperties(Math, MathShims);
// Chrome < 40 sinh returns ∞ for large numbers
defineProperty(Math, 'sinh', MathShims.sinh, Math.sinh(710) === Infinity);
// Chrome < 40 cosh returns ∞ for large numbers
defineProperty(Math, 'cosh', MathShims.cosh, Math.cosh(710) === Infinity);
// IE 11 TP has an imprecise log1p: reports Math.log1p(-1e-17) as 0
defineProperty(Math, 'log1p', MathShims.log1p, Math.log1p(-1e-17) !== -1e-17);
// IE 11 TP has an imprecise asinh: reports Math.asinh(-1e7) as not exactly equal to -Math.asinh(1e7)
defineProperty(Math, 'asinh', MathShims.asinh, Math.asinh(-1e7) !== -Math.asinh(1e7));
// Chrome < 54 asinh returns ∞ for large numbers and should not
defineProperty(Math, 'asinh', MathShims.asinh, Math.asinh(1e+300) === Infinity);
// Chrome < 54 atanh incorrectly returns 0 for large numbers
defineProperty(Math, 'atanh', MathShims.atanh, Math.atanh(1e-300) === 0);
// Chrome 40 has an imprecise Math.tanh with very small numbers
defineProperty(Math, 'tanh', MathShims.tanh, Math.tanh(-2e-17) !== -2e-17);
// Chrome 40 loses Math.acosh precision with high numbers
Expand Down
20 changes: 20 additions & 0 deletions test/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ describe('Math', function () {
expect(Math.asinh(1e7)).to.almostEqual(16.811242831518268);
expect(Math.asinh(-1e7)).to.almostEqual(-16.811242831518268);
});

it('is correct for extreme non-infinities', function () {
expect(Math.asinh(1e+300)).not.to.equal(Infinity);
expect(Math.asinh(1e+300)).to.almostEqual(691.4686750787736);
});
});

describe('.atanh()', function () {
Expand Down Expand Up @@ -143,6 +148,11 @@ describe('Math', function () {
expect(Math.atanh(-0.5)).to.almostEqual(-0.5493061443340549);
expect(Math.atanh(0.444)).to.almostEqual(0.47720201260109457);
});

it('is correct for extreme non-infinities', function () {
expect(Math.atanh(1e-300)).not.to.equal(0);
expect(Math.atanh(1e-300)).to.almostEqual(1e-300);
});
});

describe('.cbrt()', function () {
Expand Down Expand Up @@ -291,6 +301,11 @@ describe('Math', function () {
expect(Math.cosh(-23)).to.almostEqual(4872401723.1244513000, 1e-5);
expect(Math.cosh(-2e-17)).to.equal(1);
});

it('is correct for extreme non-infinities', function () {
expect(Math.cosh(710)).not.to.equal(Infinity);
expect(Math.cosh(710) / 1e+308).to.almostEqual(1.1169973830808557);
});
});

describe('.expm1()', function () {
Expand Down Expand Up @@ -576,6 +591,11 @@ describe('Math', function () {
expect(Math.sinh(2)).to.almostEqual(3.6268604078470186);
expect(Math.sinh(-2e-17)).to.equal(-2e-17);
});

it('is correct for extreme non-infinities', function () {
expect(Math.sinh(710)).not.to.equal(Infinity);
expect(Math.sinh(710) / 1e+308).to.almostEqual(1.1169973830808557);
});
});

describe('.tanh()', function () {
Expand Down

0 comments on commit ec76d49

Please sign in to comment.