Skip to content
This repository has been archived by the owner on Aug 4, 2022. It is now read-only.

Commit

Permalink
Bug 896264 - Implement Math.hypot(). r=jorendorff.
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidCaabeiro committed Oct 2, 2013
1 parent e02564e commit dd295d0
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 71 deletions.
82 changes: 23 additions & 59 deletions js/src/jsmath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1273,74 +1273,41 @@ js::math_atanh(JSContext *cx, unsigned argc, Value *vp)
return math_function<math_atanh_impl>(cx, argc, vp);
}

// Math.hypot is disabled pending the resolution of spec issues (bug 896264).
#if 0
#if !HAVE_HYPOT
double hypot(double x, double y)
{
if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y))
return PositiveInfinity();

if (mozilla::IsNaN(x) || mozilla::IsNaN(y))
return GenericNaN();

double xabs = mozilla::Abs(x);
double yabs = mozilla::Abs(y);

double min = std::min(xabs, yabs);
double max = std::max(xabs, yabs);

if (min == 0) {
return max;
} else {
double u = min / max;
return max * sqrt(1 + u * u);
}
}
#endif

double
js::math_hypot_impl(double x, double y)
{
#ifdef XP_WIN
// On Windows, hypot(NaN, Infinity) is NaN. ES6 requires Infinity.
if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y))
return PositiveInfinity();
#endif
return hypot(x, y);
}

bool
js::math_hypot(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() < 2) {
args.rval().setNumber(GenericNaN());
return true;
}

double x, y;
if (!ToNumber(cx, args[0], &x))
return false;
bool isInfinite = false;
bool isNaN = false;

if (!ToNumber(cx, args[1], &y))
return false;
double scale = 0;
double sumsq = 1;

if (args.length() == 2) {
args.rval().setNumber(math_hypot_impl(x, y));
return true;
}
for (unsigned i = 0; i < args.length(); i++) {
double x;
if (!ToNumber(cx, args[i], &x))
return false;

/* args.length() > 2 */
double z;
if (!ToNumber(cx, args[2], &z)) {
return false;
isInfinite |= mozilla::IsInfinite(x);
isNaN |= mozilla::IsNaN(x);

double xabs = mozilla::Abs(x);

if (scale < xabs) {
sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
scale = xabs;
} else if (scale != 0) {
sumsq += (xabs / scale) * (xabs / scale);
}
}

args.rval().setNumber(math_hypot_impl(math_hypot_impl(x, y), z));
double result = isInfinite ? PositiveInfinity() :
isNaN ? GenericNaN() :
scale * sqrt(sumsq);
args.rval().setNumber(result);
return true;
}
#endif

#if !HAVE_TRUNC
double trunc(double x)
Expand Down Expand Up @@ -1468,10 +1435,7 @@ static const JSFunctionSpec math_static_methods[] = {
JS_FN("acosh", math_acosh, 1, 0),
JS_FN("asinh", math_asinh, 1, 0),
JS_FN("atanh", math_atanh, 1, 0),
// Math.hypot is disabled pending the resolution of spec issues (bug 896264).
#if 0
JS_FN("hypot", math_hypot, 2, 0),
#endif
JS_FN("trunc", math_trunc, 1, 0),
JS_FN("sign", math_sign, 1, 0),
JS_FN("cbrt", math_cbrt, 1, 0),
Expand Down
3 changes: 0 additions & 3 deletions js/src/jsmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,8 @@ math_asinh(JSContext *cx, unsigned argc, js::Value *vp);
extern bool
math_atanh(JSContext *cx, unsigned argc, js::Value *vp);

// Math.hypot is disabled pending the resolution of spec issues (bug 896264).
#if 0
extern bool
math_hypot(JSContext *cx, unsigned argc, Value *vp);
#endif

extern bool
math_trunc(JSContext *cx, unsigned argc, Value *vp);
Expand Down
17 changes: 13 additions & 4 deletions js/src/tests/ecma_6/Math/hypot-approx.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
// |reftest| skip
// Math.hypot is disabled pending the resolution of spec issues (bug 896264).

for (var i = -20; i < 20; i++) {
assertEq(Math.hypot(+0, i), Math.abs(i));
assertEq(Math.hypot(-0, i), Math.abs(i));
}

assertNear(Math.hypot(1e300, 1e300), 1.4142135623730952e+300);
// The implementation must avoid underlow.
// The implementation must avoid overflow, where possible.
// The implementation must minimise rounding errors.

assertNear(Math.hypot(1e-300, 1e-300), 1.414213562373095e-300);
assertNear(Math.hypot(1e-300, 1e-300, 1e-300), 1.732050807568877e-300);

assertNear(Math.hypot(1e-3, 1e-3, 1e-3), 0.0017320508075688772);

assertNear(Math.hypot(1e300, 1e300), 1.4142135623730952e+300);
assertNear(Math.hypot(1e100, 1e200, 1e300), 1e300);

assertNear(Math.hypot(1e3, 1e-3), 1000.0000000005);
assertNear(Math.hypot(1e-300, 1e300), 1e300);
assertNear(Math.hypot(1e3, 1e-3, 1e3, 1e-3), 1414.2135623738021555);

for (var i = 1, j = 1; i < 2; i += 0.05, j += 0.05)
assertNear(Math.hypot(i, j), Math.sqrt(i * i + j * j));
Expand Down
9 changes: 4 additions & 5 deletions js/src/tests/ecma_6/Math/hypot-exact.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// |reftest| skip
// Math.hypot is disabled pending the resolution of spec issues (bug 896264).

// Properties of Math.hypot that are guaranteed by the spec.

// If no arguments are passed, the result is +0.
assertEq(Math.hypot(), +0);

// If any argument is +∞, the result is +∞.
// If any argument is −∞, the result is +∞.
for (var inf of [Infinity, -Infinity]) {
Expand Down Expand Up @@ -31,8 +31,7 @@ for (var inf of [Infinity, -Infinity]) {
}

// If no argument is +∞ or −∞, and any argument is NaN, the result is NaN.
assertEq(Math.hypot(), NaN);
assertEq(Math.hypot(1), NaN);
assertEq(Math.hypot(NaN), NaN);

assertEq(Math.hypot(NaN, 0), NaN);
assertEq(Math.hypot(0, NaN), NaN);
Expand Down

0 comments on commit dd295d0

Please sign in to comment.