-
Notifications
You must be signed in to change notification settings - Fork 385
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
- #338
- #338
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1310,29 +1310,29 @@ | |
var MathShims = { | ||
acosh: function acosh(value) { | ||
var x = Number(value); | ||
if (Number.isNaN(x) || value < 1) { return NaN; } | ||
if (x === 1) { return 0; } | ||
if (x === Infinity) { return x; } | ||
return Math.log(x / Math.E + Math.sqrt(x + 1) * Math.sqrt(x - 1) / Math.E) + 1; | ||
if (Math.abs(x) > Math.sqrt(2 / Number.EPSILON)) { | ||
return Math.log(x) + Math.LN2; | ||
} | ||
return Math.log1p(x - 1 + Math.sqrt(x - 1) * Math.sqrt(x + 1)); | ||
}, | ||
|
||
asinh: function asinh(value) { | ||
var x = Number(value); | ||
if (x === 0 || !globalIsFinite(x)) { | ||
if (x === 0) { | ||
return x; | ||
} | ||
return x < 0 ? -Math.asinh(-x) : Math.log(x + Math.sqrt(x * x + 1)); | ||
if (x < 0) { | ||
return -Math.asinh(-x); | ||
} | ||
if (Math.abs(x) > Math.sqrt(2 / Number.EPSILON)) { | ||
return Math.log(x) + Math.LN2; | ||
} | ||
return Math.log1p(x + Math.expm1(Math.log1p(x * x) / 2)); | ||
}, | ||
|
||
atanh: function atanh(value) { | ||
var x = Number(value); | ||
if (Number.isNaN(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 * Math.log((1 + x) / (1 - x)); | ||
return (Math.log1p(x) - Math.log1p(-x)) / 2; | ||
}, | ||
|
||
cbrt: function cbrt(value) { | ||
|
@@ -1362,12 +1362,10 @@ | |
|
||
cosh: function cosh(value) { | ||
var x = Number(value); | ||
if (x === 0) { return 1; } // +0 or -0 | ||
if (Number.isNaN(x)) { return NaN; } | ||
if (!globalIsFinite(x)) { return Infinity; } | ||
if (x < 0) { x = -x; } | ||
if (x > 21) { return Math.exp(x) / 2; } | ||
return (Math.exp(x) + Math.exp(-x)) / 2; | ||
if (x === 0) { | ||
return 1; | ||
} | ||
return (Math.exp(x - 1) + Math.exp(-x - 1)) * (Math.E / 2); | ||
}, | ||
|
||
expm1: function expm1(value) { | ||
|
@@ -1447,24 +1445,20 @@ | |
|
||
sinh: function sinh(value) { | ||
var x = Number(value); | ||
if (!globalIsFinite(x) || x === 0) { return x; } | ||
|
||
if (Math.abs(x) < 1) { | ||
return (Math.expm1(x) - Math.expm1(-x)) / 2; | ||
} | ||
return (Math.exp(x - 1) - Math.exp(-x - 1)) * Math.E / 2; | ||
return (Math.exp(x - 1) - Math.exp(-x - 1)) * (Math.E / 2); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. brackets are important here - Math.sinh(710) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine with adding these parentheses (brackets are There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. mathematically - not, but from floating point perspective - should, because of overflows
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah ha, good point :-) |
||
}, | ||
|
||
tanh: function tanh(value) { | ||
var x = Number(value); | ||
if (Number.isNaN(x) || x === 0) { return x; } | ||
if (x === Infinity) { return 1; } | ||
if (x === -Infinity) { return -1; } | ||
var a = Math.expm1(x); | ||
var b = Math.expm1(-x); | ||
if (a === Infinity) { return 1; } | ||
if (b === Infinity) { return -1; } | ||
return (a - b) / (Math.exp(x) + Math.exp(-x)); | ||
var a = Math.expm1(2 * x); | ||
var b = Math.expm1(2 * x) + 2; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why am I calculating There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ha, good question |
||
if (a === Infinity && b === Infinity) { | ||
return 1; | ||
} | ||
return a / b; | ||
}, | ||
|
||
trunc: function trunc(value) { | ||
|
@@ -1505,21 +1499,34 @@ | |
} | ||
}; | ||
defineProperties(Math, MathShims); | ||
// 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); | ||
|
||
var BINARY_64_EPSILON = Math.pow(2, -52); | ||
// https://code.google.com/p/v8/issues/detail?id=3468 | ||
var MATH_EXP_ERROR = Math.abs(1 - Math.exp(704.6589) / 1.0702171200481775e+306) / BINARY_64_EPSILON; | ||
var isAlmostEqual = function (a, b) { | ||
return Math.abs(1 - a / b) / BINARY_64_EPSILON < MATH_EXP_ERROR + 8; | ||
}; | ||
|
||
// Chrome 40-43 | ||
defineProperty(Math, 'acosh', MathShims.acosh, !isAlmostEqual(Math.acosh(1.0000000000000009), 4.214684851089403e-8)); | ||
// 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 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 | ||
defineProperty(Math, 'acosh', MathShims.acosh, Math.acosh(Number.MAX_VALUE) === Infinity); | ||
// Chrome 40-43 | ||
defineProperty(Math, 'asinh', MathShims.asinh, !isAlmostEqual(Math.asinh(1.7976931348623157e+308), 710.475860073944)); | ||
// Chrome 40-43 | ||
defineProperty(Math, 'atanh', MathShims.atanh, !isAlmostEqual(Math.atanh(Math.pow(2, -1021)), Math.pow(2, -1021))); | ||
// Firefox 38 on Windows | ||
defineProperty(Math, 'cbrt', MathShims.cbrt, Math.abs(1 - Math.cbrt(1e-300) / 1e-100) / Number.EPSILON > 8); | ||
// node 0.11 has an imprecise Math.sinh with very small numbers | ||
defineProperty(Math, 'sinh', MathShims.sinh, Math.sinh(-2e-17) !== -2e-17); | ||
defineProperty(Math, 'cbrt', MathShims.cbrt, !isAlmostEqual(Math.cbrt(1e-300), 1e-100)); | ||
// node 0.12 | ||
defineProperty(Math, 'cosh', MathShims.cosh, !isAlmostEqual(Math.cosh(710), 1.1169973830808555e+308)); | ||
// FF 35 on Linux reports 22025.465794806725 for Math.expm1(10) | ||
var expm1OfTen = Math.expm1(10); | ||
defineProperty(Math, 'expm1', MathShims.expm1, expm1OfTen > 22025.465794806719 || expm1OfTen < 22025.4657948067165168); | ||
defineProperty(Math, 'expm1', MathShims.expm1, !isAlmostEqual(Math.expm1(10), 22025.465794806718)); | ||
// IE 11 TP has an imprecise log1p: reports Math.log1p(-1e-17) as 0 | ||
defineProperty(Math, 'log1p', MathShims.log1p, !isAlmostEqual(Math.log1p(-1e-17), -1e-17)); | ||
// node 0.11 has an imprecise Math.sinh with very small numbers | ||
defineProperty(Math, 'sinh', MathShims.sinh, !isAlmostEqual(Math.sinh(-2e-17), -2e-17)); | ||
// Chrome 40 on Windows has an imprecise Math.tanh with very small numbers | ||
defineProperty(Math, 'tanh', MathShims.tanh, !isAlmostEqual(Math.tanh(-2e-17), -2e-17)); | ||
|
||
var origMathRound = Math.round; | ||
// breaks in e.g. Safari 8, Internet Explorer 11, Opera 12 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,17 @@ | ||
/*global describe, it, expect, require */ | ||
|
||
var Assertion = expect().constructor; | ||
Assertion.prototype.almostEqual = function (obj, precision) { | ||
|
||
var EPSILON = Math.pow(2, -52); | ||
var MIN_VALUE = Math.pow(2, -1022); | ||
var MAX_VALUE = Math.pow(2, 1023) * (2 - EPSILON); | ||
|
||
var MATH_EXP_ERROR = Math.abs(1 - Math.exp(704.6589) / 1.0702171200481775e+306) / EPSILON; | ||
Assertion.prototype.almostEqual = function (expected) { | ||
'use strict'; | ||
|
||
var allowedDiff = precision || 1e-11; | ||
return this.within(obj - allowedDiff, obj + allowedDiff); | ||
var C = (expected < 0 ? -1 : (expected > 0 ? 1 : expected)) * (MATH_EXP_ERROR + 8) * EPSILON; | ||
return this.within(expected * (1 - C), expected * (1 + C)); | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is not ideal, I do not know how to access private "this.__flags.object" and I did not test it well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ljharb , I fixed by using |
||
|
||
describe('Math', function () { | ||
|
@@ -27,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; | ||
|
||
(typeof process !== 'undefined' && process.env.NO_ES6_SHIM ? it.skip : it)('is on the exported object', function () { | ||
var exported = require('../'); | ||
|
@@ -50,15 +55,16 @@ describe('Math', function () { | |
it('should be correct', function () { | ||
expect(numberIsNaN(Math.acosh(NaN))).to.equal(true); | ||
expect(numberIsNaN(Math.acosh(0))).to.equal(true); | ||
expect(numberIsNaN(Math.acosh(0.9999999))).to.equal(true); | ||
expect(numberIsNaN(Math.acosh(1 - EPSILON / 2))).to.equal(true); | ||
expect(numberIsNaN(Math.acosh(-1e300))).to.equal(true); | ||
expect(Math.acosh(1e+99)).to.almostEqual(228.64907138697046); | ||
expect(isPositiveZero(Math.acosh(1))).to.equal(true); | ||
expect(Math.acosh(Infinity)).to.equal(Infinity); | ||
expect(Math.acosh(1234)).to.almostEqual(7.811163220849231); | ||
expect(Math.acosh(8.88)).to.almostEqual(2.8737631531629235); | ||
expect(Math.acosh(1e160)).to.almostEqual(369.10676205960726); | ||
expect(Math.acosh(Number.MAX_VALUE)).to.almostEqual(710.4758600739439); | ||
expect(Math.acosh(MAX_VALUE)).to.almostEqual(710.4758600739439); | ||
expect(Math.acosh(1 + EPSILON)).to.almostEqual(2.1073424255447017e-8); | ||
}); | ||
}); | ||
|
||
|
@@ -95,6 +101,10 @@ describe('Math', function () { | |
expect(Math.asinh(1e150)).to.almostEqual(346.0809111296668); | ||
expect(Math.asinh(1e7)).to.almostEqual(16.811242831518268); | ||
expect(Math.asinh(-1e7)).to.almostEqual(-16.811242831518268); | ||
expect(Math.asinh(MAX_VALUE)).to.almostEqual(710.4758600739439); | ||
expect(Math.asinh(-MAX_VALUE)).to.almostEqual(-710.4758600739439); | ||
expect(Math.asinh(MIN_VALUE * 2)).to.almostEqual(MIN_VALUE * 2); | ||
expect(Math.asinh(-MIN_VALUE * 2)).to.almostEqual(-MIN_VALUE * 2); | ||
}); | ||
}); | ||
|
||
|
@@ -113,14 +123,18 @@ describe('Math', function () { | |
|
||
it('should be correct', function () { | ||
expect(numberIsNaN(Math.atanh(NaN))).to.equal(true); | ||
expect(numberIsNaN(Math.atanh(-1.00000001))).to.equal(true); | ||
expect(numberIsNaN(Math.atanh(1.00000001))).to.equal(true); | ||
expect(numberIsNaN(Math.atanh(-1e300))).to.equal(true); | ||
expect(numberIsNaN(Math.atanh(1e300))).to.equal(true); | ||
expect(numberIsNaN(Math.atanh(-1 - EPSILON))).to.equal(true); | ||
expect(numberIsNaN(Math.atanh(1 + EPSILON))).to.equal(true); | ||
expect(numberIsNaN(Math.atanh(-MAX_VALUE))).to.equal(true); | ||
expect(numberIsNaN(Math.atanh(MAX_VALUE))).to.equal(true); | ||
expect(Math.atanh(-1)).to.equal(-Infinity); | ||
expect(Math.atanh(1)).to.equal(Infinity); | ||
expect(isPositiveZero(Math.atanh(+0))).to.equal(true); | ||
expect(isNegativeZero(Math.atanh(-0))).to.equal(true); | ||
expect(Math.atanh(-1 + EPSILON / 2)).to.almostEqual(-18.714973875118524); | ||
expect(Math.atanh(1 - EPSILON / 2)).to.almostEqual(18.714973875118524); | ||
expect(Math.atanh(MIN_VALUE * 2)).to.almostEqual(MIN_VALUE * 2); | ||
expect(Math.atanh(-MIN_VALUE * 2)).to.almostEqual(-MIN_VALUE * 2); | ||
expect(Math.atanh(0.5)).to.almostEqual(0.5493061443340549); | ||
expect(Math.atanh(-0.5)).to.almostEqual(-0.5493061443340549); | ||
expect(Math.atanh(-0.5)).to.almostEqual(-0.5493061443340549); | ||
|
@@ -151,10 +165,10 @@ describe('Math', function () { | |
expect(Math.cbrt(8)).to.almostEqual(2); | ||
expect(Math.cbrt(-1000)).to.almostEqual(-10); | ||
expect(Math.cbrt(1000)).to.almostEqual(10); | ||
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); | ||
expect(Math.cbrt(-MIN_VALUE)).to.almostEqual(-2.8126442852362615e-103); | ||
expect(Math.cbrt(MIN_VALUE)).to.almostEqual(2.8126442852362615e-103); | ||
expect(Math.cbrt(-MAX_VALUE)).to.almostEqual(-5.643803094122361e+102); | ||
expect(Math.cbrt(MAX_VALUE)).to.almostEqual(5.643803094122361e+102); | ||
}); | ||
}); | ||
|
||
|
@@ -260,13 +274,13 @@ describe('Math', function () { | |
}); | ||
|
||
it('should be correct', function () { | ||
// Overridden precision values here are for Chrome, as of v25.0.1364.172 | ||
// Broadened slightly for Firefox 31 | ||
expect(Math.cosh(12)).to.almostEqual(81377.39571257407, 9e-11); | ||
expect(Math.cosh(22)).to.almostEqual(1792456423.065795780980053377, 1e-5); | ||
expect(Math.cosh(12)).to.almostEqual(81377.39571257407); | ||
expect(Math.cosh(22)).to.almostEqual(1792456423.065795780980053377); | ||
expect(Math.cosh(-10)).to.almostEqual(11013.23292010332313972137); | ||
expect(Math.cosh(-23)).to.almostEqual(4872401723.1244513000, 1e-5); | ||
expect(Math.cosh(-2e-17)).to.equal(1); | ||
expect(Math.cosh(-23)).to.almostEqual(4872401723.1244513000); | ||
expect(Math.cosh(-2e-17)).to.almostEqual(1); | ||
expect(Math.cosh(710)).to.almostEqual(1.1169973830808555e+308); | ||
expect(Math.cosh(-710)).to.almostEqual(1.1169973830808555e+308); | ||
}); | ||
}); | ||
|
||
|
@@ -551,7 +565,11 @@ describe('Math', function () { | |
expect(Math.sinh(-Infinity)).to.equal(-Infinity); | ||
expect(Math.sinh(-5)).to.almostEqual(-74.20321057778875); | ||
expect(Math.sinh(2)).to.almostEqual(3.6268604078470186); | ||
expect(Math.sinh(-2e-17)).to.equal(-2e-17); | ||
expect(Math.sinh(-2e-17)).to.almostEqual(-2e-17); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In which browsers is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ljharb in a browser of 2150 year, which have Number.EPSILON < 2e-17 * 2e-17 / 3 |
||
expect(Math.sinh(MIN_VALUE * 2)).to.almostEqual(MIN_VALUE * 2); | ||
expect(Math.sinh(-MIN_VALUE * 2)).to.almostEqual(-MIN_VALUE * 2); | ||
expect(Math.sinh(710)).to.almostEqual(1.1169973830808555e+308); | ||
expect(Math.sinh(-710)).to.almostEqual(-1.1169973830808555e+308); | ||
}); | ||
}); | ||
|
||
|
@@ -582,7 +600,8 @@ describe('Math', function () { | |
expect(Math.tanh(-Infinity)).to.equal(-1); | ||
expect(Math.tanh(90)).to.almostEqual(1); | ||
expect(Math.tanh(10)).to.almostEqual(0.9999999958776927); | ||
expect(Math.tanh(-2e-17)).to.equal(-2e-17); | ||
expect(Math.tanh(MIN_VALUE * 2)).to.almostEqual(MIN_VALUE * 2); | ||
expect(Math.tanh(-MIN_VALUE * 2)).to.almostEqual(-MIN_VALUE * 2); | ||
}); | ||
}); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just about to post an issue recommending the use of a range-reduction method similar to that used for
Math.acosh
in the current shim:However, this solution looks less hackish (also, I'm not sure whether the function call to
square()
would be worth it if the argument contains an operator, compared to running the inner operations twice).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lewisje ,
Hi! Why is it better? Could you provide more details? Is
Math.log(x) + Math.log(2)
not accurate for "big"x
? I cannot find anyx
, for which your impl. produce too much different values.Your implementation is not accurate for small values (Math.pow(2, -1021)),
What I do not like in my is :
if (x * x + 1 === x * x) {
. Possibly, I should usex > Math.sqrt(2 / Number.EPSILON)
here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, by "this" I meant the line I was commenting on; that is, your idea is better than mine and I hope it is merged in.