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

accuracy of Math.cbrt, Math.expm1, Math.log1p #392

Closed
Yaffle opened this issue Jan 13, 2015 · 19 comments
Closed

accuracy of Math.cbrt, Math.expm1, Math.log1p #392

Yaffle opened this issue Jan 13, 2015 · 19 comments

Comments

@Yaffle
Copy link
Contributor

Yaffle commented Jan 13, 2015

var error = function (x, value) {
  return Math.abs(1 - x / value) / 2.220446049250313e-16; // error in number of ulps (http://en.wikipedia.org/wiki/Unit_in_the_last_place)
};

Firefox < 48 on Windows:

https://bugzilla.mozilla.org/show_bug.cgi?id=933257
https://bugzilla.mozilla.org/show_bug.cgi?id=897634

error(Math.log1p(0.0006516748007717887), 0.0006514625529548382) > 500;
error(Math.expm1(0.00001), 0.000010000050000166668) > 43000;
error(Math.cbrt(-3.9355663969868275e+307), -3.401489081587483e+102) > 50;
error(Math.asinh(-0.0005), -0.000499999979166669) > 100; // (caused by issues with `expm1` and `log1p` on Windows)
error(Math.atanh(-0.0005), -0.0005000000416666729) > 100; // (caused by issues with `expm1` and `log1p` on Windows)

Firefox

Math.exp(1) !== Math.E

Chrome < 54 on Windows:

https://code.google.com/p/v8/issues/detail?id=3468
https://code.google.com/p/v8/issues/detail?id=3509

error(Math.exp(-707.9999999999998), 3.30755300363916e-308) > 250;
Math.acosh(1e+308) === Infinity;
Math.asinh(1e+300) === Infinity;
Math.atanh(1e-300) === 0;

Konqueror 4.13 (Ubuntu):

https://github.com/KDE/kjs/blob/master/src/kjs/math_object.cpp

   Math.expm1 == undefined && Math.exmp1 != undefined
   Math.LOG10E !== 0.4342944819032518 // 0.43429448190325176
   Math.abs(+0) === 0 && 1 / Math.abs(+0) < 0
   Math.hypot.length === 0
   Math.hypot(1e200, 1e200) === 1 / 0 // overflow and underflow issues

   Math.asinh == undefined;
   Math.clz32 == undefeined;
   Math.imul() == undefined;

buggy Math.round in Safari 10 / Mobile Safari 10 / IE 11 / Edge 13 / Opera 12 / Konqueror 4.13:

Math.round(0.49999999999999994) !== 0

Math.pow issue in Firefox, Chrome, Safari, Edge when the second argument is an integer:

https://code.google.com/p/v8/issues/detail?id=3599
https://bugzilla.mozilla.org/show_bug.cgi?id=618251

error(Math.pow(10, 305), 1e+305) > 2;
error(Math.pow(10, 308), 1e+308) > 2;
error(Math.pow(9767, 77), 1.627839019440664e+307) > 8
error(Math.pow(1 + Math.pow(2, -27), Math.pow(2, 31) - 1), 8886109.924647752) > Math.pow(2, 28)

Chrome Mobile 52 (Android 5.1):

Math.pow(11, Math.pow(11, 9)) === 0;

Mobile Safari 10 (iOS 9.2):

Math.expm1(709.782712893384) === -1;
// note: Math.expm1(709.7827128933839) === 1.7976931348620688e+308

es6-shim:

nothing

core-js:

error(Math.acosh(1 + Number.EPSILON), 2.1073424255447017e-8) > 1e+7;
Math.asinh(1e-300) === 0;
Math.asinh(1e+300) === Infinity;
Math.atanh(1e-300) === 0;
error(Math.cbrt(1e-300), 1e-100) > 50;
Math.cosh(710) === Infinity;
error(Math.expm1(1e-6), 0.0000010000005000001665) > 1e+5;
error(Math.log1p(1e-8), 9.999999950000001e-9) > 1e+7;
+ Math.sinh, Math.tanh (because of expm1)

Traceur AND MDN's polyfills:

Math.acosh(1e+308) === Infinity;
error(Math.acosh(1 + Number.EPSILON), 2.1073424255447017e-8) > 1e+7;
Math.asinh(1e-300) === 0;
Math.asinh(1e+300) === Infinity;
Math.atanh(1e-300) === 0;
error(Math.cbrt(1e-300), 1e-100) > 50;
Math.cosh(710) === Infinity;
Math.sinh(710) === Infinity;
Math.sinh(1e-300) === 0;
Math.tanh(1e-300) === 0;
Math.tanh(1e+300) !== Math.tanh(1e+300);
Math.expm1(1e-300) === 0;
Math.log1p(1e-300) === 0;

Closure:

Math.acosh(1e+308) === Infinity;
error(Math.acosh(1 + Number.EPSILON), 2.1073424255447017e-8) > 1e+7;
Math.asinh(1e-300) === 0;
Math.asinh(1e+300) === Infinity;
error(Math.cbrt(1e-300), 1e-100) > 50;
Math.cosh(710) === Infinity;
Math.sinh(710) === Infinity;
Math.sinh(1e-300) === 0;
Math.tanh(1e-300) === 0;
Math.hypot(0, 0) !== Math.hypot(0, 0)
Math.hypot(1, Infinity) !== Math.hypot(1, Infinity)
@ljharb
Copy link
Member

ljharb commented Jan 13, 2015

Nothing in the spec defines the implementation or epsilon of the Math functions ( https://people.mozilla.org/~jorendorff/es6-draft.html#sec-function-properties-of-the-math-object ) It specifically says "For other argument values, these functions are intended to compute approximations to the results of familiar mathematical functions, but some latitude is allowed in the choice of approximation algorithms."

What compatibility is necessary to test here?

@kangax
Copy link
Collaborator

kangax commented Jan 13, 2015

@Yaffle you should bring it up on es-discuss

@zloirock
Copy link
Collaborator

@Yaffle You can propose better implementation.

@Yaffle Yaffle closed this as completed Jan 15, 2015
@zloirock
Copy link
Collaborator

@Yaffle I'm serious. I'm not specialist in math. You can propose your implementation math functions for these libraries.

@zloirock
Copy link
Collaborator

@Yaffle Thanks for Math.tanh and Math.acosh. I fixed them (but not forced for v8) and Math.hypot.

ljharb added a commit to paulmillr/es6-shim that referenced this issue Jan 16, 2015
ljharb added a commit to paulmillr/es6-shim that referenced this issue Jan 16, 2015
@ljharb
Copy link
Member

ljharb commented Jan 17, 2015

Thanks - can you provide some specific examples for each one that I can use as failing tests?

@ljharb
Copy link
Member

ljharb commented Jan 17, 2015

Math.round is ES5, not ES6, so please do file an es5-shim issue about that one :-) ES6 has Math.fround, which may have similar issues.

zloirock added a commit to zloirock/core-js that referenced this issue Jan 17, 2015
@zloirock
Copy link
Collaborator

Thanks!

@ljharb
Copy link
Member

ljharb commented Apr 13, 2015

@Yaffle PRs with the tests to es5-shim/es6-shim, and probably also compat-table, would be very helpful - the implementations are great but the tests are the important part :-)

@ljharb
Copy link
Member

ljharb commented Apr 14, 2015

@Yaffle thanks!

isExpm1OK, isLog1pOK, isAcoshOK, and isCbrtOK are already true in shimmed Firefox 37, Chrome 41, and Safari 8.

isAsinhOK and isAtanhOK are the only falses, and only in Chrome 41, they pass in FF/Safari.

I've filed an es6-shim issue for those two.

@ljharb
Copy link
Member

ljharb commented Jun 2, 2015

Ah - new PRs welcome, let's make sure the comments include both the OS and browser version.

@ljharb
Copy link
Member

ljharb commented Jan 8, 2016

@Yaffle thanks - it's hard to see what the problem is, could you perhaps indicate what the value should be, and what it is in the browser that deviates?

@ljharb
Copy link
Member

ljharb commented Jan 8, 2016

Thanks, I filed #724 - if you could add comments with other Konqueror issues that'd be great, since I don't have a way to test it.

@kangax
Copy link
Collaborator

kangax commented Jan 8, 2016

I would consider removing Konqueror. Seems like an ancient browser with very specific audience. I'm struggling to figure out who would still be using it.

@ljharb
Copy link
Member

ljharb commented Jan 9, 2016

That's also a fair point - there may not be any value in including it.

@ljharb
Copy link
Member

ljharb commented Mar 10, 2016

It would be helpful if you could indicate what the correct/best answer should be in all these cases as well

@ljharb
Copy link
Member

ljharb commented Mar 10, 2016

@Yaffle lol wat? why did you delete all your comments?

@Yaffle
Copy link
Contributor Author

Yaffle commented May 20, 2016

@ljharb , I have merged everything into the first comment.

@Yaffle
Copy link
Contributor Author

Yaffle commented Aug 16, 2016

  Math.log2 = function (x) {
    x = Number(x);
    return Math.log(x) * Math.LOG2E;
  };
  Math.log10 = function (x) {
    x = Number(x);
    return Math.log(x) * Math.LOG10E;
  };

  Math.hypot = function (x, y) {
    // https://bugzilla.mozilla.org/show_bug.cgi?id=896264#c28
    var max = 0;
    var s = 0;
    for (var i = 0; i < arguments.length; i += 1) {
      var z = Number(arguments[i]);
      var a = Math.abs(z);
      if (a === 1 / 0) {
        return a;
      }
      if (a !== 0) {
        if (a > max) {
          s *= (max / a) * (max / a);
          max = a;
        }
        s += (a / max) * (a / max);
      }
    }
    return max * Math.sqrt(s);
  };
  Math.cbrt = function (x) {
    x = Number(x);
    // http://en.wikipedia.org/wiki/Cube_root#Numerical_methods
    if (x !== x || x === 0 || x === -1 / 0 || x === +1 / 0) {
      return x;
    }
    var s = x < 0 ? -1 : +1;
    var a = Math.abs(x);
    var y0 = Math.exp(Math.log(a) / 3);
    return s * (a / (y0 * y0) + 2 * y0) / 3;
  };

  Math.log1p = function (x) {
    x = Number(x);
    // https://bugs.ecmascript.org/show_bug.cgi?id=309#c16
    if (x !== x || x === 0 || x === +1 / 0) {
      return x;
    }
    var x0 = 1 + x;
    var y0 = Math.log(x0);
    return x0 > 0 ? y0 + (1 - x0 + x) / x0 : y0;
  };
  Math.expm1 = function (x) {
    x = Number(x);
    // https://en.wikipedia.org/wiki/Newton%27s_method
    if (x !== x || x === 0) {
      return x;
    }
    var y0 = Math.exp(x);
    var x0 = Math.log(y0);
    return y0 - 1 + (x0 === -1 / 0 || x0 === +1 / 0 ? 0 : (x - x0) * y0);
  };

  Math.acosh = function (x) {
    x = Number(x);
    if (x < 1) {
      return 0 / 0;
    }
    if (x < 2) {
      return Math.log1p(x - 1 + Math.sqrt(1 - 1 / (x * x)) * x);
    }
    return Math.log1p(x / 2 + Math.sqrt(1 - 1 / (x * x)) * x / 2 - 1) + 1 / Math.LOG2E;
  };
  Math.asinh = function (x) {
    x = Number(x);
    if (x !== x || x === 0) {
      return x;
    }
    var s = x < 0 ? -1 : +1;
    var a = Math.abs(x);
    if (a < 1) {
      return s * Math.log1p(a + a * a / (Math.sqrt(a * a + 1) + 1));
    }
    return s * (Math.log1p(a / 2 + Math.sqrt(1 + 1 / (a * a)) * a / 2 - 1) + 1 / Math.LOG2E);
  };
  Math.atanh = function (x) {
    x = Number(x);
    if (x !== x || x === 0) {
      return x;
    }
    var s = x < 0 ? -1 : +1;
    var a = Math.abs(x);
    return s * Math.log1p(2 * a / (1 - a)) / 2;
  };
  Math.cosh = function (x) {
    x = Number(x);
    if (x !== x) {
      return x;
    }
    var a = Math.abs(x);
    var t = Math.expm1(a - 1) + 1;
    return (t + 1 / (t * Math.E * Math.E)) * (Math.E / 2);
  };
  Math.sinh = function (x) {
    x = Number(x);
    if (x !== x || x === 0) {
      return x;
    }
    var s = x < 0 ? -1 : +1;
    var a = Math.abs(x);
    if (a < 1) {
      var u = Math.expm1(a);
      return s * u * (1 + 1 / (u + 1)) / 2;
    }
    var t = Math.expm1(a - 1) + 1;
    return s * (t - 1 / (t * Math.E * Math.E)) * (Math.E / 2);
  };
  Math.tanh = function (x) {
    x = Number(x);
    if (x !== x || x === 0) {
      return x;
    }
    var s = x < 0 ? -1 : +1;
    var a = Math.abs(x);
    if (a < 1) {
      var u = Math.expm1(2 * a);
      return s * u / (u + 2);
    }
    var t = Math.expm1(2 * a) + 1;
    return s / (1 + 2 / (t - 1));
  };


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants