Skip to content

Commit

Permalink
[Tests] acosh: fix precision (#338)
Browse files Browse the repository at this point in the history
 - [Tests] add `to.haveULPDistance` assertion
 - [Refactor] add `hasULPDistance` helper
 - use assertion/helper for `cbrt`
  • Loading branch information
ljharb committed Apr 7, 2017
1 parent 488b496 commit 6d65367
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 5 deletions.
17 changes: 15 additions & 2 deletions es6-shim.js
Expand Up @@ -1886,7 +1886,13 @@
if (numberIsNaN(x) || value < 1) { return NaN; }
if (x === 1) { return 0; }
if (x === Infinity) { return x; }
return _log((x / E) + (_sqrt(x + 1) * _sqrt(x - 1) / E)) + 1;

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

asinh: function asinh(value) {
Expand Down Expand Up @@ -2064,6 +2070,11 @@
return sign * result;
}
};

var hasULPDistance = function hasULPDistance(result, expected, distance) {
return _abs(1 - (result / expected)) / Number.EPSILON > (distance || 8);

This comment has been minimized.

Copy link
@Yaffle

Yaffle Apr 8, 2017

Contributor

@ljharb , it seems, the operator "<" should be used istead of ">". Current code overrides cbrt and acosh in Firefox 52.
Other things work fine, thanks.

This comment has been minimized.

Copy link
@ljharb

ljharb Apr 8, 2017

Author Collaborator

Thanks, fixed in 8d7aec1

};

defineProperties(Math, MathShims);
// Chrome < 40 sinh returns ∞ for large numbers
defineProperty(Math, 'sinh', MathShims.sinh, Math.sinh(710) === Infinity);
Expand All @@ -2081,8 +2092,10 @@
defineProperty(Math, 'tanh', MathShims.tanh, Math.tanh(-2e-17) !== -2e-17);
// Chrome 40 loses Math.acosh precision with high numbers
defineProperty(Math, 'acosh', MathShims.acosh, Math.acosh(Number.MAX_VALUE) === Infinity);
// Chrome < 54 has an inaccurate acosh for EPSILON deltas
defineProperty(Math, 'acosh', MathShims.acosh, !hasULPDistance(Math.acosh(1 + Number.EPSILON), Math.sqrt(2 * Number.EPSILON)));
// Firefox 38 on Windows
defineProperty(Math, 'cbrt', MathShims.cbrt, Math.abs(1 - (Math.cbrt(1e-300) / 1e-100)) / Number.EPSILON > 8);
defineProperty(Math, 'cbrt', MathShims.cbrt, !hasULPDistance(Math.cbrt(1e-300), 1e-100));
// node 0.11 has an imprecise Math.sinh with very small numbers
defineProperty(Math, 'sinh', MathShims.sinh, Math.sinh(-2e-17) !== -2e-17);
// FF 35 on Linux reports 22025.465794806725 for Math.expm1(10)
Expand Down
23 changes: 20 additions & 3 deletions test/math.js
@@ -1,3 +1,5 @@
var EPSILON = Number.EPSILON || 2.2204460492503130808472633361816e-16;

var Assertion = expect().constructor;
Assertion.prototype.almostEqual = function (obj, precision) {
'use strict';
Expand All @@ -6,6 +8,11 @@ Assertion.prototype.almostEqual = function (obj, precision) {
return this.within(obj - allowedDiff, obj + allowedDiff);
};

Assertion.prototype.haveULPDistance = function (expected, distance) {
var actual = this._obj;
return this.above(Math.abs(1 - (actual / expected)) / EPSILON, distance);
};

describe('Math', function () {
var functionsHaveNames = (function foo() {}).name === 'foo';
var ifFunctionsHaveNamesIt = functionsHaveNames ? it : xit;
Expand All @@ -26,7 +33,6 @@ describe('Math', function () {
};
var valueOfIsNaN = { valueOf: function () { return NaN; } };
var valueOfIsInfinity = { valueOf: function () { return Infinity; } };
var EPSILON = Number.EPSILON || 2.2204460492503130808472633361816e-16;

ifShimIt('is on the exported object', function () {
var exported = require('../');
Expand Down Expand Up @@ -67,7 +73,11 @@ describe('Math', function () {
});

it('works for EPSILON values near 1', function () {
expect(Math.acosh(1 + EPSILON)).to.almostEqual(Math.sqrt(2 * EPSILON));
var result = Math.acosh(1 + EPSILON);
var expected = Math.sqrt(2 * EPSILON);

expect(result).to.almostEqual(expected);
expect(result).to.haveULPDistance(expected, 8);
});
});

Expand Down Expand Up @@ -188,8 +198,15 @@ describe('Math', function () {
expect(Math.cbrt(8)).to.almostEqual(2);
expect(Math.cbrt(-1000)).to.almostEqual(-10);
expect(Math.cbrt(1000)).to.almostEqual(10);
});

it('is correct at extremes', function () {
var result = Math.cbrt(1e-300);
var expected = 1e-100;
expect(result).to.almostEqual(expected);
expect(result).to.haveULPDistance(expected, 8);

expect(Math.cbrt(-1e-300)).to.almostEqual(-1e-100);
expect(Math.cbrt(1e-300)).to.almostEqual(1e-100);
expect(Math.cbrt(-1e+300)).to.almostEqual(-1e+100);
expect(Math.cbrt(1e+300)).to.almostEqual(1e+100);
});
Expand Down

0 comments on commit 6d65367

Please sign in to comment.