Skip to content
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

Closed
wants to merge 4 commits into from
Closed

- #338

wants to merge 4 commits into from

Conversation

Yaffle
Copy link
Contributor

@Yaffle Yaffle commented Apr 17, 2015

Fixes #334.

var allowedDiff = precision || 1e-11;
return this.within(obj - allowedDiff, obj + allowedDiff);
var ok = Math.abs(1 - this.__flags.object / expected) / Number.EPSILON < MATH_EXP_ERROR + 8;
this.assert(ok, 'expected #{this} to be almost equal ' + expected, 'expected #{this} to not be almost equal' + expected);
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this.flags('object') might work better

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ljharb , I fixed by using this.within

@Yaffle
Copy link
Contributor Author

Yaffle commented Apr 18, 2015

@ljharb , OK, what do you think now?

if (abs < BINARY_32_MIN_VALUE) {
return sign * roundTiesToEven(abs / BINARY_32_MIN_VALUE / BINARY_32_EPSILON) * BINARY_32_MIN_VALUE * BINARY_32_EPSILON;
}
// Veltkamp's splitting (?)
var a = (1 + BINARY_32_EPSILON / Number.EPSILON) * abs;
var result = a - (a - abs);
if (result > BINARY_32_MAX_VALUE || numberIsNaN(result)) {
if (result > BINARY_32_MAX_VALUE || Number.isNaN(result)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert this change - it's there intentionally, so that the ordering of shims doesn't matter. Same everywhere it used to say numberIsNaN

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, result !== result is better, because it has no dependency on both Number.isNaN and numberIsNaN

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, I'd prefer numberIsNaN so that the NaN checks can all be routed through the same code path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I followed this rule

@ljharb
Copy link
Collaborator

ljharb commented Apr 19, 2015

The tests look good, and fail as expected on Chrome 42, node 0.8/0.10/0.11/0.12, and iojs 1.7.

return (a - b) / (Math.exp(x) + Math.exp(-x));
var a = Math.expm1(2 * x);
var b = Math.expm1(2 * x) + 2;
return a === b ? 1 : a / b;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for what values is a === b && a / b !== 1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Infinity

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh snap again, good call. Is this faster enough that it's worth the far less clear version, than the previous which had explicit returns for ± Infinity?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no,
if (a === Infinity) { return 1; } could be used

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think keeping the explicit return checks for ± Infinity are a good idea - probably also the initial NaN and 0 checks too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ljharb , I cannot understand how explicit checks can help, I want to have number of "if" branches as small as possible. But I have replaced ternary operator with more readable if (a === Infinity && b === Infinity) { ... } .

@Yaffle Yaffle changed the title Math.acosh, Math.asinh, Math.atanh, ... Math.acosh, Math.asinh, Math.atanh, Math.sinh, Math.cosh, Math.sinh, Math.tanh Apr 29, 2015
@Yaffle Yaffle changed the title Math.acosh, Math.asinh, Math.atanh, Math.sinh, Math.cosh, Math.sinh, Math.tanh Math.acosh, Math.asinh, Math.atanh, Math.cosh, Math.sinh, Math.tanh Apr 29, 2015
@@ -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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In which browsers is Math.sinh(-2e-17) not precisely equal to -2e-17?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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

@ljharb
Copy link
Collaborator

ljharb commented May 11, 2015

Thanks, this is looking great. I'll test it in a number of browsers before merging it.

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;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why am I calculating Math.expm1(2 * x) twice?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ha, good question

@Yaffle
Copy link
Contributor Author

Yaffle commented Jun 4, 2015

any chance to finish this?

@ljharb
Copy link
Collaborator

ljharb commented Jun 4, 2015

Absolutely - it'll need to be freshly rebased on top of master, and then I'll do a fresh round of browser testing - but I don't think it needs much more work.

@Yaffle
Copy link
Contributor Author

Yaffle commented Mar 8, 2016

Math.acosh, Math.asinh, Math.cosh, Math.sinh are still not accurate ...

Math.acosh(1 + Number.EPSILON)
Math.asinh(Number.MAX_VALUE)
var EXP_NO_OVERFLOW = 709.782712893384; // Math.log(Number.MAX_VALUE)
Math.cosh(EXP_NO_OVERFLOW + 1 / Math.LOG2E);
Math.sinh(EXP_NO_OVERFLOW + 1 / Math.LOG2E);

@ljharb
Copy link
Collaborator

ljharb commented Mar 8, 2016

@Yaffle I'd love to bring this PR in if you want to freshly rebase it - it's been about 10 months :-)

@Yaffle Yaffle closed this Mar 8, 2016
@ljharb ljharb mentioned this pull request Mar 8, 2016
Closed
@Yaffle Yaffle changed the title Math.acosh, Math.asinh, Math.atanh, Math.cosh, Math.sinh, Math.tanh - Mar 9, 2016
@Yaffle
Copy link
Contributor Author

Yaffle commented Apr 6, 2017

~2 years

@ljharb
Copy link
Collaborator

ljharb commented Apr 6, 2017

@Yaffle yes, it's been 1 year since you closed the PR and deleted the branch without rebasing it, and since nobody else seems to understand this topic as well as you do, it will basically never be fixed.

I'd be delighted if you reopened the PR and rebased the branch, then it could get in very soon.

@Yaffle
Copy link
Contributor Author

Yaffle commented Apr 6, 2017

I have put all implementations of not precise ES6 Math functions to the comment at
compat-table/compat-table#392 (comment)

@ljharb
Copy link
Collaborator

ljharb commented Apr 6, 2017

Yes, thank you, but a dump of implementations doesn't help me change anything. What is helpful is a set of test cases, justified with evidence (browser results, spec steps, etc), and then also an implementation if a good one isn't apparent.

@Yaffle
Copy link
Contributor Author

Yaffle commented Apr 6, 2017

@ljharb,

  1. a set of test cases:
    Math.asinh(1e+300) should not be equal to +∞;
    Math.atanh(1e-300) should not be equal to +0;
    Math.cosh(710) should not be equal to +∞;
    Math.sinh(710) should not be equal to +∞;
    Math.acosh(1 + Number.EPSILON) should be near Math.sqrt(2 * Number.EPSILON)
  2. in good implementations (Chrome 54+, Firefox 48+, Safari 10+, Edge):
    Math.asinh(1e+300) returns ~691.4686750787736;
    Math.atanh(1e-300) returns 1e-300;
    Math.cosh(710) returns ~1.1169973830808557e+308;
    Math.sinh(710) returns ~1.1169973830808557e+308;
    Math.acosh(1 + Number.EPSILON) returns ~2.1073424255447017e-8;
    And so the relative error is very big.
    Note: Chrome < 54 on Windows fails tests for Math.asinh, Math.atanh and Math.acosh.
    Chrome < 40 ? on Windows fails tests for Math.cosh and Math.sinh.
  3. The implementations are available at accuracy of Math.cbrt, Math.expm1, Math.log1p compat-table/compat-table#392 (comment)

@ljharb
Copy link
Collaborator

ljharb commented Apr 6, 2017

Thank you, that is much more helpful!

ljharb added a commit that referenced this pull request Apr 7, 2017
@ljharb
Copy link
Collaborator

ljharb commented Apr 7, 2017

@Yaffle thanks, those first 4 are taken care of in ec76d49; i missed the edit for the 5th and will follow up with a fix for that.

@ljharb
Copy link
Collaborator

ljharb commented Apr 7, 2017

I'm unable to reproduce the acosh test failure on Chrome 53, 50, even down to 40.

ljharb added a commit that referenced this pull request Apr 7, 2017
@Yaffle
Copy link
Contributor Author

Yaffle commented Apr 7, 2017

@ljharb , the test for acosh might look similar to test for cbrt at

defineProperty(Math, 'cbrt', MathShims.cbrt, Math.abs(1 - (Math.cbrt(1e-300) / 1e-100)) / Number.EPSILON > 8);
:

Math.abs(1 - (Math.acosh(1 + Number.EPSILON) / Math.sqrt(2 * Number.EPSILON))) / Number.EPSILON > 8

Math.abs(1 - actual / expected) / Number.EPSILON returns something like "ulp" distance - https://en.wikipedia.org/wiki/Unit_in_the_last_place , while "almostEquals" measures only the absolute error.

@ljharb
Copy link
Collaborator

ljharb commented Apr 7, 2017

thanks, i'll update that.

@Yaffle
Copy link
Contributor Author

Yaffle commented Apr 7, 2017

@ljharb, thanks

ljharb added a commit that referenced this pull request Apr 7, 2017
 - [Tests] add `to.haveULPDistance` assertion
 - [Refactor] add `hasULPDistance` helper
 - use assertion/helper for `cbrt`
@ljharb
Copy link
Collaborator

ljharb commented Apr 7, 2017

k, should be all set.

ljharb added a commit that referenced this pull request Apr 7, 2017
 - [Tests] add `to.haveULPDistance` assertion
 - [Refactor] add `hasULPDistance` helper
 - use assertion/helper for `cbrt`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Chrome 41: Math.asinh and Math.atanh are imprecise
3 participants