diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 222db17eed26..dd80828e65ba 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -131,18 +131,28 @@ js::math_abs(JSContext* cx, unsigned argc, Value* vp) return math_abs_handle(cx, args[0], args.rval()); } +#if defined(SOLARIS) && defined(__GNUC__) +#define ACOS_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return GenericNaN(); +#else +#define ACOS_IF_OUT_OF_RANGE(x) +#endif + double js::math_acos_impl(MathCache* cache, double x) { + ACOS_IF_OUT_OF_RANGE(x); return cache->lookup(fdlibm::acos, x, MathCache::Acos); } double js::math_acos_uncached(double x) { + ACOS_IF_OUT_OF_RANGE(x); return fdlibm::acos(x); } +#undef ACOS_IF_OUT_OF_RANGE + bool js::math_acos(JSContext* cx, unsigned argc, Value* vp) { @@ -166,18 +176,28 @@ js::math_acos(JSContext* cx, unsigned argc, Value* vp) return true; } +#if defined(SOLARIS) && defined(__GNUC__) +#define ASIN_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return GenericNaN(); +#else +#define ASIN_IF_OUT_OF_RANGE(x) +#endif + double js::math_asin_impl(MathCache* cache, double x) { + ASIN_IF_OUT_OF_RANGE(x); return cache->lookup(fdlibm::asin, x, MathCache::Asin); } double js::math_asin_uncached(double x) { + ASIN_IF_OUT_OF_RANGE(x); return fdlibm::asin(x); } +#undef ASIN_IF_OUT_OF_RANGE + bool js::math_asin(JSContext* cx, unsigned argc, Value* vp) { @@ -239,6 +259,30 @@ js::math_atan(JSContext* cx, unsigned argc, Value* vp) double js::ecmaAtan2(double y, double x) { +#if defined(_MSC_VER) + /* + * MSVC's atan2 does not yield the result demanded by ECMA when both x + * and y are infinite. + * - The result is a multiple of pi/4. + * - The sign of y determines the sign of the result. + * - The sign of x determines the multiplicator, 1 or 3. + */ + if (IsInfinite(y) && IsInfinite(x)) { + double z = js_copysign(M_PI / 4, y); + if (x < 0) + z *= 3; + return z; + } +#endif + +#if defined(SOLARIS) && defined(__GNUC__) + if (y == 0) { + if (IsNegativeZero(x)) + return js_copysign(M_PI, y); + if (x == 0) + return y; + } +#endif return fdlibm::atan2(y, x); } @@ -269,6 +313,10 @@ js::math_atan2(JSContext* cx, unsigned argc, Value* vp) double js::math_ceil_impl(double x) { +#ifdef __APPLE__ + if (x < 0 && x > -1.0) + return js_copysign(0, -1); +#endif return fdlibm::ceil(x); } @@ -355,18 +403,34 @@ js::math_cos(JSContext* cx, unsigned argc, Value* vp) return true; } +#ifdef _WIN32 +#define EXP_IF_OUT_OF_RANGE(x) \ + if (!IsNaN(x)) { \ + if (x == PositiveInfinity()) \ + return PositiveInfinity(); \ + if (x == NegativeInfinity()) \ + return 0.0; \ + } +#else +#define EXP_IF_OUT_OF_RANGE(x) +#endif + double js::math_exp_impl(MathCache* cache, double x) { + EXP_IF_OUT_OF_RANGE(x); return cache->lookup(fdlibm::exp, x, MathCache::Exp); } double js::math_exp_uncached(double x) { + EXP_IF_OUT_OF_RANGE(x); return fdlibm::exp(x); } +#undef EXP_IF_OUT_OF_RANGE + bool js::math_exp(JSContext* cx, unsigned argc, Value* vp) { @@ -480,18 +544,28 @@ js::math_fround(JSContext* cx, unsigned argc, Value* vp) return RoundFloat32(cx, args[0], args.rval()); } +#if defined(SOLARIS) && defined(__GNUC__) +#define LOG_IF_OUT_OF_RANGE(x) if (x < 0) return GenericNaN(); +#else +#define LOG_IF_OUT_OF_RANGE(x) +#endif + double js::math_log_impl(MathCache* cache, double x) { + LOG_IF_OUT_OF_RANGE(x); return cache->lookup(math_log_uncached, x, MathCache::Log); } double js::math_log_uncached(double x) { + LOG_IF_OUT_OF_RANGE(x); return fdlibm::log(x); } +#undef LOG_IF_OUT_OF_RANGE + bool js::math_log_handle(JSContext* cx, HandleValue val, MutableHandleValue res) { @@ -983,6 +1057,13 @@ js::math_log10(JSContext* cx, unsigned argc, Value* vp) return math_function(cx, argc, vp); } +#if !HAVE_LOG2 +double log2(double x) +{ + return log(x) / M_LN2; +} +#endif + double js::math_log2_impl(MathCache* cache, double x) { @@ -1001,24 +1082,73 @@ js::math_log2(JSContext* cx, unsigned argc, Value* vp) return math_function(cx, argc, vp); } +#if !HAVE_LOG1P +double log1p(double x) +{ + if (fabs(x) < 1e-4) { + /* + * Use Taylor approx. log(1 + x) = x - x^2 / 2 + x^3 / 3 - x^4 / 4 with error x^5 / 5 + * Since |x| < 10^-4, |x|^5 < 10^-20, relative error less than 10^-16 + */ + double z = -(x * x * x * x) / 4 + (x * x * x) / 3 - (x * x) / 2 + x; + return z; + } else { + /* For other large enough values of x use direct computation */ + return log(1.0 + x); + } +} +#endif + +#ifdef __APPLE__ +// Ensure that log1p(-0) is -0. +#define LOG1P_IF_OUT_OF_RANGE(x) if (x == 0) return x; +#else +#define LOG1P_IF_OUT_OF_RANGE(x) +#endif + double js::math_log1p_impl(MathCache* cache, double x) { + LOG1P_IF_OUT_OF_RANGE(x); return cache->lookup(fdlibm::log1p, x, MathCache::Log1p); } double js::math_log1p_uncached(double x) { + LOG1P_IF_OUT_OF_RANGE(x); return fdlibm::log1p(x); } +#undef LOG1P_IF_OUT_OF_RANGE + bool js::math_log1p(JSContext* cx, unsigned argc, Value* vp) { return math_function(cx, argc, vp); } +#if !HAVE_EXPM1 +double expm1(double x) +{ + /* Special handling for -0 */ + if (x == 0.0) + return x; + + if (fabs(x) < 1e-5) { + /* + * Use Taylor approx. exp(x) - 1 = x + x^2 / 2 + x^3 / 6 with error x^4 / 24 + * Since |x| < 10^-5, |x|^4 < 10^-20, relative error less than 10^-15 + */ + double z = (x * x * x) / 6 + (x * x) / 2 + x; + return z; + } else { + /* For other large enough values of x use direct computation */ + return exp(x) - 1.0; + } +} +#endif + double js::math_expm1_impl(MathCache* cache, double x) { @@ -1037,6 +1167,17 @@ js::math_expm1(JSContext* cx, unsigned argc, Value* vp) return math_function(cx, argc, vp); } +#if !HAVE_SQRT1PM1 +/* This algorithm computes sqrt(1+x)-1 for small x */ +double sqrt1pm1(double x) +{ + if (fabs(x) > 0.75) + return sqrt(1 + x) - 1; + + return expm1(log1p(x) / 2); +} +#endif + double js::math_cosh_impl(MathCache* cache, double x) { @@ -1091,6 +1232,37 @@ js::math_tanh(JSContext* cx, unsigned argc, Value* vp) return math_function(cx, argc, vp); } +#if !HAVE_ACOSH +double acosh(double x) +{ + const double SQUARE_ROOT_EPSILON = sqrt(std::numeric_limits::epsilon()); + + if ((x - 1) >= SQUARE_ROOT_EPSILON) { + if (x > 1 / SQUARE_ROOT_EPSILON) { + /* + * http://functions.wolfram.com/ElementaryFunctions/ArcCosh/06/01/06/01/0001/ + * approximation by laurent series in 1/x at 0+ order from -1 to 0 + */ + return log(x) + M_LN2; + } else if (x < 1.5) { + // This is just a rearrangement of the standard form below + // devised to minimize loss of precision when x ~ 1: + double y = x - 1; + return log1p(y + sqrt(y * y + 2 * y)); + } else { + // http://functions.wolfram.com/ElementaryFunctions/ArcCosh/02/ + return log(x + sqrt(x * x - 1)); + } + } else { + // see http://functions.wolfram.com/ElementaryFunctions/ArcCosh/06/01/04/01/0001/ + double y = x - 1; + // approximation by taylor series in y at 0 up to order 2. + // If x is less than 1, sqrt(2 * y) is NaN and the result is NaN. + return sqrt(2 * y) * (1 - y / 12 + 3 * y * y / 160); + } +} +#endif + double js::math_acosh_impl(MathCache* cache, double x) { @@ -1109,16 +1281,59 @@ js::math_acosh(JSContext* cx, unsigned argc, Value* vp) return math_function(cx, argc, vp); } +#if !HAVE_ASINH +// Bug 899712 - gcc incorrectly rewrites -asinh(-x) to asinh(x) when overriding +// asinh. +static double my_asinh(double x) +{ + const double SQUARE_ROOT_EPSILON = sqrt(std::numeric_limits::epsilon()); + const double FOURTH_ROOT_EPSILON = sqrt(SQUARE_ROOT_EPSILON); + + if (x >= FOURTH_ROOT_EPSILON) { + if (x > 1 / SQUARE_ROOT_EPSILON) + // http://functions.wolfram.com/ElementaryFunctions/ArcSinh/06/01/06/01/0001/ + // approximation by laurent series in 1/x at 0+ order from -1 to 1 + return M_LN2 + log(x) + 1 / (4 * x * x); + else if (x < 0.5) + return log1p(x + sqrt1pm1(x * x)); + else + return log(x + sqrt(x * x + 1)); + } else if (x <= -FOURTH_ROOT_EPSILON) { + return -my_asinh(-x); + } else { + // http://functions.wolfram.com/ElementaryFunctions/ArcSinh/06/01/03/01/0001/ + // approximation by taylor series in x at 0 up to order 2 + double result = x; + + if (fabs(x) >= SQUARE_ROOT_EPSILON) { + double x3 = x * x * x; + // approximation by taylor series in x at 0 up to order 4 + result -= x3 / 6; + } + + return result; + } +} +#endif + double js::math_asinh_impl(MathCache* cache, double x) { +#ifdef HAVE_ASINH return cache->lookup(fdlibm::asinh, x, MathCache::Asinh); +#else + return cache->lookup(my_asinh, x, MathCache::Asinh); +#endif } double js::math_asinh_uncached(double x) { +#ifdef HAVE_ASINH return fdlibm::asinh(x); +#else + return my_asinh(x); +#endif } bool @@ -1127,6 +1342,34 @@ js::math_asinh(JSContext* cx, unsigned argc, Value* vp) return math_function(cx, argc, vp); } +#if !HAVE_ATANH +double atanh(double x) +{ + const double EPSILON = std::numeric_limits::epsilon(); + const double SQUARE_ROOT_EPSILON = sqrt(EPSILON); + const double FOURTH_ROOT_EPSILON = sqrt(SQUARE_ROOT_EPSILON); + + if (fabs(x) >= FOURTH_ROOT_EPSILON) { + // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/02/ + if (fabs(x) < 0.5) + return (log1p(x) - log1p(-x)) / 2; + + return log((1 + x) / (1 - x)) / 2; + } else { + // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/06/01/03/01/ + // approximation by taylor series in x at 0 up to order 2 + double result = x; + + if (fabs(x) >= SQUARE_ROOT_EPSILON) { + double x3 = x * x * x; + result += x3 / 3; + } + + return result; + } +} +#endif + double js::math_atanh_impl(MathCache* cache, double x) { @@ -1149,6 +1392,15 @@ js::math_atanh(JSContext* cx, unsigned argc, Value* vp) double js::ecmaHypot(double x, double y) { +#ifdef XP_WIN + /* + * Workaround MS hypot bug, where hypot(Infinity, NaN or Math.MIN_VALUE) + * is NaN, not Infinity. + */ + if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y)) { + return mozilla::PositiveInfinity(); + } +#endif return fdlibm::hypot(x, y); } @@ -1290,6 +1542,19 @@ js::math_sign(JSContext* cx, unsigned argc, Value* vp) return math_function(cx, argc, vp); } +#if !HAVE_CBRT +double cbrt(double x) +{ + if (x > 0) { + return pow(x, 1.0 / 3.0); + } else if (x == 0) { + return x; + } else { + return -pow(-x, 1.0 / 3.0); + } +} +#endif + double js::math_cbrt_impl(MathCache* cache, double x) { diff --git a/js/src/moz.build b/js/src/moz.build index 54596d59e0cd..eee8d6dc5c21 100644 --- a/js/src/moz.build +++ b/js/src/moz.build @@ -661,6 +661,7 @@ if CONFIG['_MSC_VER']: CXXFLAGS += ['-wd4661'] CXXFLAGS += ['-we4067', '-we4258', '-we4275'] CXXFLAGS += ['-wd4146'] # FIXME: unary minus operator applied to unsigned type (bug 1229189) + CXXFLAGS += ['-wd4273'] # FIXME: inconsistent dll linkage (bug 1229666) if CONFIG['OS_ARCH'] not in ('WINNT', 'HP-UX'): OS_LIBS += [ diff --git a/js/src/old-configure.in b/js/src/old-configure.in index 92f5353bd0ff..3409f22e2124 100644 --- a/js/src/old-configure.in +++ b/js/src/old-configure.in @@ -1702,7 +1702,9 @@ fi dnl Checks for math functions. dnl ======================================================== +AC_CHECK_LIB(m, sin) AC_CHECK_LIB(m, __sincos, AC_DEFINE(HAVE_SINCOS)) +AC_CHECK_FUNCS([log2 log1p expm1 sqrt1pm1 acosh asinh atanh cbrt]) dnl check for wcrtomb/mbrtowc