js/src/jsmath.cpp

Thu, 15 Jan 2015 15:55:04 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:55:04 +0100
branch
TOR_BUG_9701
changeset 9
a63d609f5ebe
permissions
-rw-r--r--

Back out 97036ab72558 which inappropriately compared turds to third parties.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
michael@0 2 * vim: set ts=8 sts=4 et sw=4 tw=99:
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /*
michael@0 8 * JS math package.
michael@0 9 */
michael@0 10
michael@0 11 #include "jsmath.h"
michael@0 12
michael@0 13 #include "mozilla/Constants.h"
michael@0 14 #include "mozilla/FloatingPoint.h"
michael@0 15 #include "mozilla/MathAlgorithms.h"
michael@0 16 #include "mozilla/MemoryReporting.h"
michael@0 17
michael@0 18 #include <algorithm> // for std::max
michael@0 19 #include <fcntl.h>
michael@0 20
michael@0 21 #ifdef XP_UNIX
michael@0 22 # include <unistd.h>
michael@0 23 #endif
michael@0 24
michael@0 25 #include "jsapi.h"
michael@0 26 #include "jsatom.h"
michael@0 27 #include "jscntxt.h"
michael@0 28 #include "jscompartment.h"
michael@0 29 #include "jslibmath.h"
michael@0 30 #include "jstypes.h"
michael@0 31 #include "prmjtime.h"
michael@0 32
michael@0 33 #include "jsobjinlines.h"
michael@0 34
michael@0 35 using namespace js;
michael@0 36
michael@0 37 using mozilla::Abs;
michael@0 38 using mozilla::NumberEqualsInt32;
michael@0 39 using mozilla::NumberIsInt32;
michael@0 40 using mozilla::ExponentComponent;
michael@0 41 using mozilla::FloatingPoint;
michael@0 42 using mozilla::IsFinite;
michael@0 43 using mozilla::IsInfinite;
michael@0 44 using mozilla::IsNaN;
michael@0 45 using mozilla::IsNegative;
michael@0 46 using mozilla::IsNegativeZero;
michael@0 47 using mozilla::PositiveInfinity;
michael@0 48 using mozilla::NegativeInfinity;
michael@0 49 using JS::ToNumber;
michael@0 50 using JS::GenericNaN;
michael@0 51
michael@0 52 static const JSConstDoubleSpec math_constants[] = {
michael@0 53 {M_E, "E", 0, {0,0,0}},
michael@0 54 {M_LOG2E, "LOG2E", 0, {0,0,0}},
michael@0 55 {M_LOG10E, "LOG10E", 0, {0,0,0}},
michael@0 56 {M_LN2, "LN2", 0, {0,0,0}},
michael@0 57 {M_LN10, "LN10", 0, {0,0,0}},
michael@0 58 {M_PI, "PI", 0, {0,0,0}},
michael@0 59 {M_SQRT2, "SQRT2", 0, {0,0,0}},
michael@0 60 {M_SQRT1_2, "SQRT1_2", 0, {0,0,0}},
michael@0 61 {0,0,0,{0,0,0}}
michael@0 62 };
michael@0 63
michael@0 64 MathCache::MathCache() {
michael@0 65 memset(table, 0, sizeof(table));
michael@0 66
michael@0 67 /* See comments in lookup(). */
michael@0 68 JS_ASSERT(IsNegativeZero(-0.0));
michael@0 69 JS_ASSERT(!IsNegativeZero(+0.0));
michael@0 70 JS_ASSERT(hash(-0.0) != hash(+0.0));
michael@0 71 }
michael@0 72
michael@0 73 size_t
michael@0 74 MathCache::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
michael@0 75 {
michael@0 76 return mallocSizeOf(this);
michael@0 77 }
michael@0 78
michael@0 79 const Class js::MathClass = {
michael@0 80 js_Math_str,
michael@0 81 JSCLASS_HAS_CACHED_PROTO(JSProto_Math),
michael@0 82 JS_PropertyStub, /* addProperty */
michael@0 83 JS_DeletePropertyStub, /* delProperty */
michael@0 84 JS_PropertyStub, /* getProperty */
michael@0 85 JS_StrictPropertyStub, /* setProperty */
michael@0 86 JS_EnumerateStub,
michael@0 87 JS_ResolveStub,
michael@0 88 JS_ConvertStub
michael@0 89 };
michael@0 90
michael@0 91 bool
michael@0 92 js_math_abs(JSContext *cx, unsigned argc, Value *vp)
michael@0 93 {
michael@0 94 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 95
michael@0 96 if (args.length() == 0) {
michael@0 97 args.rval().setNaN();
michael@0 98 return true;
michael@0 99 }
michael@0 100
michael@0 101 double x;
michael@0 102 if (!ToNumber(cx, args[0], &x))
michael@0 103 return false;
michael@0 104
michael@0 105 double z = Abs(x);
michael@0 106 args.rval().setNumber(z);
michael@0 107 return true;
michael@0 108 }
michael@0 109
michael@0 110 #if defined(SOLARIS) && defined(__GNUC__)
michael@0 111 #define ACOS_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return GenericNaN();
michael@0 112 #else
michael@0 113 #define ACOS_IF_OUT_OF_RANGE(x)
michael@0 114 #endif
michael@0 115
michael@0 116 double
michael@0 117 js::math_acos_impl(MathCache *cache, double x)
michael@0 118 {
michael@0 119 ACOS_IF_OUT_OF_RANGE(x);
michael@0 120 return cache->lookup(acos, x);
michael@0 121 }
michael@0 122
michael@0 123 double
michael@0 124 js::math_acos_uncached(double x)
michael@0 125 {
michael@0 126 ACOS_IF_OUT_OF_RANGE(x);
michael@0 127 return acos(x);
michael@0 128 }
michael@0 129
michael@0 130 #undef ACOS_IF_OUT_OF_RANGE
michael@0 131
michael@0 132 bool
michael@0 133 js::math_acos(JSContext *cx, unsigned argc, Value *vp)
michael@0 134 {
michael@0 135 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 136
michael@0 137 if (args.length() == 0) {
michael@0 138 args.rval().setNaN();
michael@0 139 return true;
michael@0 140 }
michael@0 141
michael@0 142 double x;
michael@0 143 if (!ToNumber(cx, args[0], &x))
michael@0 144 return false;
michael@0 145
michael@0 146 MathCache *mathCache = cx->runtime()->getMathCache(cx);
michael@0 147 if (!mathCache)
michael@0 148 return false;
michael@0 149
michael@0 150 double z = math_acos_impl(mathCache, x);
michael@0 151 args.rval().setDouble(z);
michael@0 152 return true;
michael@0 153 }
michael@0 154
michael@0 155 #if defined(SOLARIS) && defined(__GNUC__)
michael@0 156 #define ASIN_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return GenericNaN();
michael@0 157 #else
michael@0 158 #define ASIN_IF_OUT_OF_RANGE(x)
michael@0 159 #endif
michael@0 160
michael@0 161 double
michael@0 162 js::math_asin_impl(MathCache *cache, double x)
michael@0 163 {
michael@0 164 ASIN_IF_OUT_OF_RANGE(x);
michael@0 165 return cache->lookup(asin, x);
michael@0 166 }
michael@0 167
michael@0 168 double
michael@0 169 js::math_asin_uncached(double x)
michael@0 170 {
michael@0 171 ASIN_IF_OUT_OF_RANGE(x);
michael@0 172 return asin(x);
michael@0 173 }
michael@0 174
michael@0 175 #undef ASIN_IF_OUT_OF_RANGE
michael@0 176
michael@0 177 bool
michael@0 178 js::math_asin(JSContext *cx, unsigned argc, Value *vp)
michael@0 179 {
michael@0 180 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 181
michael@0 182 if (args.length() == 0) {
michael@0 183 args.rval().setNaN();
michael@0 184 return true;
michael@0 185 }
michael@0 186
michael@0 187 double x;
michael@0 188 if (!ToNumber(cx, args[0], &x))
michael@0 189 return false;
michael@0 190
michael@0 191 MathCache *mathCache = cx->runtime()->getMathCache(cx);
michael@0 192 if (!mathCache)
michael@0 193 return false;
michael@0 194
michael@0 195 double z = math_asin_impl(mathCache, x);
michael@0 196 args.rval().setDouble(z);
michael@0 197 return true;
michael@0 198 }
michael@0 199
michael@0 200 double
michael@0 201 js::math_atan_impl(MathCache *cache, double x)
michael@0 202 {
michael@0 203 return cache->lookup(atan, x);
michael@0 204 }
michael@0 205
michael@0 206 double
michael@0 207 js::math_atan_uncached(double x)
michael@0 208 {
michael@0 209 return atan(x);
michael@0 210 }
michael@0 211
michael@0 212 bool
michael@0 213 js::math_atan(JSContext *cx, unsigned argc, Value *vp)
michael@0 214 {
michael@0 215 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 216
michael@0 217 if (args.length() == 0) {
michael@0 218 args.rval().setNaN();
michael@0 219 return true;
michael@0 220 }
michael@0 221
michael@0 222 double x;
michael@0 223 if (!ToNumber(cx, args[0], &x))
michael@0 224 return false;
michael@0 225
michael@0 226 MathCache *mathCache = cx->runtime()->getMathCache(cx);
michael@0 227 if (!mathCache)
michael@0 228 return false;
michael@0 229
michael@0 230 double z = math_atan_impl(mathCache, x);
michael@0 231 args.rval().setDouble(z);
michael@0 232 return true;
michael@0 233 }
michael@0 234
michael@0 235 double
michael@0 236 js::ecmaAtan2(double y, double x)
michael@0 237 {
michael@0 238 #if defined(_MSC_VER)
michael@0 239 /*
michael@0 240 * MSVC's atan2 does not yield the result demanded by ECMA when both x
michael@0 241 * and y are infinite.
michael@0 242 * - The result is a multiple of pi/4.
michael@0 243 * - The sign of y determines the sign of the result.
michael@0 244 * - The sign of x determines the multiplicator, 1 or 3.
michael@0 245 */
michael@0 246 if (IsInfinite(y) && IsInfinite(x)) {
michael@0 247 double z = js_copysign(M_PI / 4, y);
michael@0 248 if (x < 0)
michael@0 249 z *= 3;
michael@0 250 return z;
michael@0 251 }
michael@0 252 #endif
michael@0 253
michael@0 254 #if defined(SOLARIS) && defined(__GNUC__)
michael@0 255 if (y == 0) {
michael@0 256 if (IsNegativeZero(x))
michael@0 257 return js_copysign(M_PI, y);
michael@0 258 if (x == 0)
michael@0 259 return y;
michael@0 260 }
michael@0 261 #endif
michael@0 262 return atan2(y, x);
michael@0 263 }
michael@0 264
michael@0 265 bool
michael@0 266 js::math_atan2(JSContext *cx, unsigned argc, Value *vp)
michael@0 267 {
michael@0 268 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 269
michael@0 270 double y;
michael@0 271 if (!ToNumber(cx, args.get(0), &y))
michael@0 272 return false;
michael@0 273
michael@0 274 double x;
michael@0 275 if (!ToNumber(cx, args.get(1), &x))
michael@0 276 return false;
michael@0 277
michael@0 278 double z = ecmaAtan2(y, x);
michael@0 279 args.rval().setDouble(z);
michael@0 280 return true;
michael@0 281 }
michael@0 282
michael@0 283 double
michael@0 284 js::math_ceil_impl(double x)
michael@0 285 {
michael@0 286 #ifdef __APPLE__
michael@0 287 if (x < 0 && x > -1.0)
michael@0 288 return js_copysign(0, -1);
michael@0 289 #endif
michael@0 290 return ceil(x);
michael@0 291 }
michael@0 292
michael@0 293 bool
michael@0 294 js::math_ceil(JSContext *cx, unsigned argc, Value *vp)
michael@0 295 {
michael@0 296 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 297
michael@0 298 if (args.length() == 0) {
michael@0 299 args.rval().setNaN();
michael@0 300 return true;
michael@0 301 }
michael@0 302
michael@0 303 double x;
michael@0 304 if (!ToNumber(cx, args[0], &x))
michael@0 305 return false;
michael@0 306
michael@0 307 double z = math_ceil_impl(x);
michael@0 308 args.rval().setNumber(z);
michael@0 309 return true;
michael@0 310 }
michael@0 311
michael@0 312 bool
michael@0 313 js::math_clz32(JSContext *cx, unsigned argc, Value *vp)
michael@0 314 {
michael@0 315 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 316
michael@0 317 if (args.length() == 0) {
michael@0 318 args.rval().setInt32(32);
michael@0 319 return true;
michael@0 320 }
michael@0 321
michael@0 322 uint32_t n;
michael@0 323 if (!ToUint32(cx, args[0], &n))
michael@0 324 return false;
michael@0 325
michael@0 326 if (n == 0) {
michael@0 327 args.rval().setInt32(32);
michael@0 328 return true;
michael@0 329 }
michael@0 330
michael@0 331 args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
michael@0 332 return true;
michael@0 333 }
michael@0 334
michael@0 335 double
michael@0 336 js::math_cos_impl(MathCache *cache, double x)
michael@0 337 {
michael@0 338 return cache->lookup(cos, x);
michael@0 339 }
michael@0 340
michael@0 341 double
michael@0 342 js::math_cos_uncached(double x)
michael@0 343 {
michael@0 344 return cos(x);
michael@0 345 }
michael@0 346
michael@0 347 bool
michael@0 348 js::math_cos(JSContext *cx, unsigned argc, Value *vp)
michael@0 349 {
michael@0 350 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 351
michael@0 352 if (args.length() == 0) {
michael@0 353 args.rval().setNaN();
michael@0 354 return true;
michael@0 355 }
michael@0 356
michael@0 357 double x;
michael@0 358 if (!ToNumber(cx, args[0], &x))
michael@0 359 return false;
michael@0 360
michael@0 361 MathCache *mathCache = cx->runtime()->getMathCache(cx);
michael@0 362 if (!mathCache)
michael@0 363 return false;
michael@0 364
michael@0 365 double z = math_cos_impl(mathCache, x);
michael@0 366 args.rval().setDouble(z);
michael@0 367 return true;
michael@0 368 }
michael@0 369
michael@0 370 #ifdef _WIN32
michael@0 371 #define EXP_IF_OUT_OF_RANGE(x) \
michael@0 372 if (!IsNaN(x)) { \
michael@0 373 if (x == PositiveInfinity<double>()) \
michael@0 374 return PositiveInfinity<double>(); \
michael@0 375 if (x == NegativeInfinity<double>()) \
michael@0 376 return 0.0; \
michael@0 377 }
michael@0 378 #else
michael@0 379 #define EXP_IF_OUT_OF_RANGE(x)
michael@0 380 #endif
michael@0 381
michael@0 382 double
michael@0 383 js::math_exp_impl(MathCache *cache, double x)
michael@0 384 {
michael@0 385 EXP_IF_OUT_OF_RANGE(x);
michael@0 386 return cache->lookup(exp, x);
michael@0 387 }
michael@0 388
michael@0 389 double
michael@0 390 js::math_exp_uncached(double x)
michael@0 391 {
michael@0 392 EXP_IF_OUT_OF_RANGE(x);
michael@0 393 return exp(x);
michael@0 394 }
michael@0 395
michael@0 396 #undef EXP_IF_OUT_OF_RANGE
michael@0 397
michael@0 398 bool
michael@0 399 js::math_exp(JSContext *cx, unsigned argc, Value *vp)
michael@0 400 {
michael@0 401 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 402
michael@0 403 if (args.length() == 0) {
michael@0 404 args.rval().setNaN();
michael@0 405 return true;
michael@0 406 }
michael@0 407
michael@0 408 double x;
michael@0 409 if (!ToNumber(cx, args[0], &x))
michael@0 410 return false;
michael@0 411
michael@0 412 MathCache *mathCache = cx->runtime()->getMathCache(cx);
michael@0 413 if (!mathCache)
michael@0 414 return false;
michael@0 415
michael@0 416 double z = math_exp_impl(mathCache, x);
michael@0 417 args.rval().setNumber(z);
michael@0 418 return true;
michael@0 419 }
michael@0 420
michael@0 421 double
michael@0 422 js::math_floor_impl(double x)
michael@0 423 {
michael@0 424 return floor(x);
michael@0 425 }
michael@0 426
michael@0 427 bool
michael@0 428 js::math_floor(JSContext *cx, unsigned argc, Value *vp)
michael@0 429 {
michael@0 430 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 431
michael@0 432 if (args.length() == 0) {
michael@0 433 args.rval().setNaN();
michael@0 434 return true;
michael@0 435 }
michael@0 436
michael@0 437 double x;
michael@0 438 if (!ToNumber(cx, args[0], &x))
michael@0 439 return false;
michael@0 440
michael@0 441 double z = math_floor_impl(x);
michael@0 442 args.rval().setNumber(z);
michael@0 443 return true;
michael@0 444 }
michael@0 445
michael@0 446 bool
michael@0 447 js::math_imul(JSContext *cx, unsigned argc, Value *vp)
michael@0 448 {
michael@0 449 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 450
michael@0 451 uint32_t a = 0, b = 0;
michael@0 452 if (args.hasDefined(0) && !ToUint32(cx, args[0], &a))
michael@0 453 return false;
michael@0 454 if (args.hasDefined(1) && !ToUint32(cx, args[1], &b))
michael@0 455 return false;
michael@0 456
michael@0 457 uint32_t product = a * b;
michael@0 458 args.rval().setInt32(product > INT32_MAX
michael@0 459 ? int32_t(INT32_MIN + (product - INT32_MAX - 1))
michael@0 460 : int32_t(product));
michael@0 461 return true;
michael@0 462 }
michael@0 463
michael@0 464 // Implements Math.fround (20.2.2.16) up to step 3
michael@0 465 bool
michael@0 466 js::RoundFloat32(JSContext *cx, Handle<Value> v, float *out)
michael@0 467 {
michael@0 468 double d;
michael@0 469 bool success = ToNumber(cx, v, &d);
michael@0 470 *out = static_cast<float>(d);
michael@0 471 return success;
michael@0 472 }
michael@0 473
michael@0 474 bool
michael@0 475 js::math_fround(JSContext *cx, unsigned argc, Value *vp)
michael@0 476 {
michael@0 477 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 478
michael@0 479 if (args.length() == 0) {
michael@0 480 args.rval().setNaN();
michael@0 481 return true;
michael@0 482 }
michael@0 483
michael@0 484 float f;
michael@0 485 if (!RoundFloat32(cx, args[0], &f))
michael@0 486 return false;
michael@0 487
michael@0 488 args.rval().setDouble(static_cast<double>(f));
michael@0 489 return true;
michael@0 490 }
michael@0 491
michael@0 492 #if defined(SOLARIS) && defined(__GNUC__)
michael@0 493 #define LOG_IF_OUT_OF_RANGE(x) if (x < 0) return GenericNaN();
michael@0 494 #else
michael@0 495 #define LOG_IF_OUT_OF_RANGE(x)
michael@0 496 #endif
michael@0 497
michael@0 498 double
michael@0 499 js::math_log_impl(MathCache *cache, double x)
michael@0 500 {
michael@0 501 LOG_IF_OUT_OF_RANGE(x);
michael@0 502 return cache->lookup(log, x);
michael@0 503 }
michael@0 504
michael@0 505 double
michael@0 506 js::math_log_uncached(double x)
michael@0 507 {
michael@0 508 LOG_IF_OUT_OF_RANGE(x);
michael@0 509 return log(x);
michael@0 510 }
michael@0 511
michael@0 512 #undef LOG_IF_OUT_OF_RANGE
michael@0 513
michael@0 514 bool
michael@0 515 js::math_log(JSContext *cx, unsigned argc, Value *vp)
michael@0 516 {
michael@0 517 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 518
michael@0 519 if (args.length() == 0) {
michael@0 520 args.rval().setNaN();
michael@0 521 return true;
michael@0 522 }
michael@0 523
michael@0 524 double x;
michael@0 525 if (!ToNumber(cx, args[0], &x))
michael@0 526 return false;
michael@0 527
michael@0 528 MathCache *mathCache = cx->runtime()->getMathCache(cx);
michael@0 529 if (!mathCache)
michael@0 530 return false;
michael@0 531
michael@0 532 double z = math_log_impl(mathCache, x);
michael@0 533 args.rval().setNumber(z);
michael@0 534 return true;
michael@0 535 }
michael@0 536
michael@0 537 bool
michael@0 538 js_math_max(JSContext *cx, unsigned argc, Value *vp)
michael@0 539 {
michael@0 540 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 541
michael@0 542 double maxval = NegativeInfinity<double>();
michael@0 543 for (unsigned i = 0; i < args.length(); i++) {
michael@0 544 double x;
michael@0 545 if (!ToNumber(cx, args[i], &x))
michael@0 546 return false;
michael@0 547 // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
michael@0 548 if (x > maxval || IsNaN(x) || (x == maxval && IsNegative(maxval)))
michael@0 549 maxval = x;
michael@0 550 }
michael@0 551 args.rval().setNumber(maxval);
michael@0 552 return true;
michael@0 553 }
michael@0 554
michael@0 555 bool
michael@0 556 js_math_min(JSContext *cx, unsigned argc, Value *vp)
michael@0 557 {
michael@0 558 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 559
michael@0 560 double minval = PositiveInfinity<double>();
michael@0 561 for (unsigned i = 0; i < args.length(); i++) {
michael@0 562 double x;
michael@0 563 if (!ToNumber(cx, args[i], &x))
michael@0 564 return false;
michael@0 565 // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
michael@0 566 if (x < minval || IsNaN(x) || (x == minval && IsNegativeZero(x)))
michael@0 567 minval = x;
michael@0 568 }
michael@0 569 args.rval().setNumber(minval);
michael@0 570 return true;
michael@0 571 }
michael@0 572
michael@0 573 // Disable PGO for Math.pow() and related functions (see bug 791214).
michael@0 574 #if defined(_MSC_VER)
michael@0 575 # pragma optimize("g", off)
michael@0 576 #endif
michael@0 577 double
michael@0 578 js::powi(double x, int y)
michael@0 579 {
michael@0 580 unsigned n = (y < 0) ? -y : y;
michael@0 581 double m = x;
michael@0 582 double p = 1;
michael@0 583 while (true) {
michael@0 584 if ((n & 1) != 0) p *= m;
michael@0 585 n >>= 1;
michael@0 586 if (n == 0) {
michael@0 587 if (y < 0) {
michael@0 588 // Unfortunately, we have to be careful when p has reached
michael@0 589 // infinity in the computation, because sometimes the higher
michael@0 590 // internal precision in the pow() implementation would have
michael@0 591 // given us a finite p. This happens very rarely.
michael@0 592
michael@0 593 double result = 1.0 / p;
michael@0 594 return (result == 0 && IsInfinite(p))
michael@0 595 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
michael@0 596 : result;
michael@0 597 }
michael@0 598
michael@0 599 return p;
michael@0 600 }
michael@0 601 m *= m;
michael@0 602 }
michael@0 603 }
michael@0 604 #if defined(_MSC_VER)
michael@0 605 # pragma optimize("", on)
michael@0 606 #endif
michael@0 607
michael@0 608 // Disable PGO for Math.pow() and related functions (see bug 791214).
michael@0 609 #if defined(_MSC_VER)
michael@0 610 # pragma optimize("g", off)
michael@0 611 #endif
michael@0 612 double
michael@0 613 js::ecmaPow(double x, double y)
michael@0 614 {
michael@0 615 /*
michael@0 616 * Use powi if the exponent is an integer-valued double. We don't have to
michael@0 617 * check for NaN since a comparison with NaN is always false.
michael@0 618 */
michael@0 619 int32_t yi;
michael@0 620 if (NumberEqualsInt32(y, &yi))
michael@0 621 return powi(x, yi);
michael@0 622
michael@0 623 /*
michael@0 624 * Because C99 and ECMA specify different behavior for pow(),
michael@0 625 * we need to wrap the libm call to make it ECMA compliant.
michael@0 626 */
michael@0 627 if (!IsFinite(y) && (x == 1.0 || x == -1.0))
michael@0 628 return GenericNaN();
michael@0 629
michael@0 630 /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */
michael@0 631 if (y == 0)
michael@0 632 return 1;
michael@0 633
michael@0 634 /*
michael@0 635 * Special case for square roots. Note that pow(x, 0.5) != sqrt(x)
michael@0 636 * when x = -0.0, so we have to guard for this.
michael@0 637 */
michael@0 638 if (IsFinite(x) && x != 0.0) {
michael@0 639 if (y == 0.5)
michael@0 640 return sqrt(x);
michael@0 641 if (y == -0.5)
michael@0 642 return 1.0 / sqrt(x);
michael@0 643 }
michael@0 644 return pow(x, y);
michael@0 645 }
michael@0 646 #if defined(_MSC_VER)
michael@0 647 # pragma optimize("", on)
michael@0 648 #endif
michael@0 649
michael@0 650 // Disable PGO for Math.pow() and related functions (see bug 791214).
michael@0 651 #if defined(_MSC_VER)
michael@0 652 # pragma optimize("g", off)
michael@0 653 #endif
michael@0 654 bool
michael@0 655 js_math_pow(JSContext *cx, unsigned argc, Value *vp)
michael@0 656 {
michael@0 657 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 658
michael@0 659 double x;
michael@0 660 if (!ToNumber(cx, args.get(0), &x))
michael@0 661 return false;
michael@0 662
michael@0 663 double y;
michael@0 664 if (!ToNumber(cx, args.get(1), &y))
michael@0 665 return false;
michael@0 666
michael@0 667 double z = ecmaPow(x, y);
michael@0 668 args.rval().setNumber(z);
michael@0 669 return true;
michael@0 670 }
michael@0 671 #if defined(_MSC_VER)
michael@0 672 # pragma optimize("", on)
michael@0 673 #endif
michael@0 674
michael@0 675 static uint64_t
michael@0 676 random_generateSeed()
michael@0 677 {
michael@0 678 union {
michael@0 679 uint8_t u8[8];
michael@0 680 uint32_t u32[2];
michael@0 681 uint64_t u64;
michael@0 682 } seed;
michael@0 683 seed.u64 = 0;
michael@0 684
michael@0 685 #if defined(XP_WIN)
michael@0 686 /*
michael@0 687 * Our PRNG only uses 48 bits, so calling rand_s() twice to get 64 bits is
michael@0 688 * probably overkill.
michael@0 689 */
michael@0 690 rand_s(&seed.u32[0]);
michael@0 691 #elif defined(XP_UNIX)
michael@0 692 /*
michael@0 693 * In the unlikely event we can't read /dev/urandom, there's not much we can
michael@0 694 * do, so just mix in the fd error code and the current time.
michael@0 695 */
michael@0 696 int fd = open("/dev/urandom", O_RDONLY);
michael@0 697 MOZ_ASSERT(fd >= 0, "Can't open /dev/urandom");
michael@0 698 if (fd >= 0) {
michael@0 699 read(fd, seed.u8, mozilla::ArrayLength(seed.u8));
michael@0 700 close(fd);
michael@0 701 }
michael@0 702 seed.u32[0] ^= fd;
michael@0 703 #else
michael@0 704 # error "Platform needs to implement random_generateSeed()"
michael@0 705 #endif
michael@0 706
michael@0 707 seed.u32[1] ^= PRMJ_Now();
michael@0 708 return seed.u64;
michael@0 709 }
michael@0 710
michael@0 711 static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
michael@0 712 static const uint64_t RNG_ADDEND = 0xBLL;
michael@0 713 static const uint64_t RNG_MASK = (1LL << 48) - 1;
michael@0 714 static const double RNG_DSCALE = double(1LL << 53);
michael@0 715
michael@0 716 /*
michael@0 717 * Math.random() support, lifted from java.util.Random.java.
michael@0 718 */
michael@0 719 static void
michael@0 720 random_initState(uint64_t *rngState)
michael@0 721 {
michael@0 722 /* Our PRNG only uses 48 bits, so squeeze our entropy into those bits. */
michael@0 723 uint64_t seed = random_generateSeed();
michael@0 724 seed ^= (seed >> 16);
michael@0 725 *rngState = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
michael@0 726 }
michael@0 727
michael@0 728 uint64_t
michael@0 729 random_next(uint64_t *rngState, int bits)
michael@0 730 {
michael@0 731 MOZ_ASSERT((*rngState & 0xffff000000000000ULL) == 0, "Bad rngState");
michael@0 732 MOZ_ASSERT(bits > 0 && bits <= 48, "bits is out of range");
michael@0 733
michael@0 734 if (*rngState == 0) {
michael@0 735 random_initState(rngState);
michael@0 736 }
michael@0 737
michael@0 738 uint64_t nextstate = *rngState * RNG_MULTIPLIER;
michael@0 739 nextstate += RNG_ADDEND;
michael@0 740 nextstate &= RNG_MASK;
michael@0 741 *rngState = nextstate;
michael@0 742 return nextstate >> (48 - bits);
michael@0 743 }
michael@0 744
michael@0 745 static inline double
michael@0 746 random_nextDouble(JSContext *cx)
michael@0 747 {
michael@0 748 uint64_t *rng = &cx->compartment()->rngState;
michael@0 749 return double((random_next(rng, 26) << 27) + random_next(rng, 27)) / RNG_DSCALE;
michael@0 750 }
michael@0 751
michael@0 752 double
michael@0 753 math_random_no_outparam(JSContext *cx)
michael@0 754 {
michael@0 755 /* Calculate random without memory traffic, for use in the JITs. */
michael@0 756 return random_nextDouble(cx);
michael@0 757 }
michael@0 758
michael@0 759 bool
michael@0 760 js_math_random(JSContext *cx, unsigned argc, Value *vp)
michael@0 761 {
michael@0 762 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 763 double z = random_nextDouble(cx);
michael@0 764 args.rval().setDouble(z);
michael@0 765 return true;
michael@0 766 }
michael@0 767
michael@0 768 double
michael@0 769 js::math_round_impl(double x)
michael@0 770 {
michael@0 771 int32_t ignored;
michael@0 772 if (NumberIsInt32(x, &ignored))
michael@0 773 return x;
michael@0 774
michael@0 775 /* Some numbers are so big that adding 0.5 would give the wrong number. */
michael@0 776 if (ExponentComponent(x) >= int_fast16_t(FloatingPoint<double>::ExponentShift))
michael@0 777 return x;
michael@0 778
michael@0 779 return js_copysign(floor(x + 0.5), x);
michael@0 780 }
michael@0 781
michael@0 782 float
michael@0 783 js::math_roundf_impl(float x)
michael@0 784 {
michael@0 785 int32_t ignored;
michael@0 786 if (NumberIsInt32(x, &ignored))
michael@0 787 return x;
michael@0 788
michael@0 789 /* Some numbers are so big that adding 0.5 would give the wrong number. */
michael@0 790 if (ExponentComponent(x) >= int_fast16_t(FloatingPoint<float>::ExponentShift))
michael@0 791 return x;
michael@0 792
michael@0 793 return js_copysign(floorf(x + 0.5f), x);
michael@0 794 }
michael@0 795
michael@0 796 bool /* ES5 15.8.2.15. */
michael@0 797 js::math_round(JSContext *cx, unsigned argc, Value *vp)
michael@0 798 {
michael@0 799 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 800
michael@0 801 if (args.length() == 0) {
michael@0 802 args.rval().setNaN();
michael@0 803 return true;
michael@0 804 }
michael@0 805
michael@0 806 double x;
michael@0 807 if (!ToNumber(cx, args[0], &x))
michael@0 808 return false;
michael@0 809
michael@0 810 double z = math_round_impl(x);
michael@0 811 args.rval().setNumber(z);
michael@0 812 return true;
michael@0 813 }
michael@0 814
michael@0 815 double
michael@0 816 js::math_sin_impl(MathCache *cache, double x)
michael@0 817 {
michael@0 818 return cache->lookup(sin, x);
michael@0 819 }
michael@0 820
michael@0 821 double
michael@0 822 js::math_sin_uncached(double x)
michael@0 823 {
michael@0 824 return sin(x);
michael@0 825 }
michael@0 826
michael@0 827 bool
michael@0 828 js::math_sin(JSContext *cx, unsigned argc, Value *vp)
michael@0 829 {
michael@0 830 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 831
michael@0 832 if (args.length() == 0) {
michael@0 833 args.rval().setNaN();
michael@0 834 return true;
michael@0 835 }
michael@0 836
michael@0 837 double x;
michael@0 838 if (!ToNumber(cx, args[0], &x))
michael@0 839 return false;
michael@0 840
michael@0 841 MathCache *mathCache = cx->runtime()->getMathCache(cx);
michael@0 842 if (!mathCache)
michael@0 843 return false;
michael@0 844
michael@0 845 double z = math_sin_impl(mathCache, x);
michael@0 846 args.rval().setDouble(z);
michael@0 847 return true;
michael@0 848 }
michael@0 849
michael@0 850 bool
michael@0 851 js_math_sqrt(JSContext *cx, unsigned argc, Value *vp)
michael@0 852 {
michael@0 853 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 854
michael@0 855 if (args.length() == 0) {
michael@0 856 args.rval().setNaN();
michael@0 857 return true;
michael@0 858 }
michael@0 859
michael@0 860 double x;
michael@0 861 if (!ToNumber(cx, args[0], &x))
michael@0 862 return false;
michael@0 863
michael@0 864 MathCache *mathCache = cx->runtime()->getMathCache(cx);
michael@0 865 if (!mathCache)
michael@0 866 return false;
michael@0 867
michael@0 868 double z = mathCache->lookup(sqrt, x);
michael@0 869 args.rval().setDouble(z);
michael@0 870 return true;
michael@0 871 }
michael@0 872
michael@0 873 double
michael@0 874 js::math_tan_impl(MathCache *cache, double x)
michael@0 875 {
michael@0 876 return cache->lookup(tan, x);
michael@0 877 }
michael@0 878
michael@0 879 double
michael@0 880 js::math_tan_uncached(double x)
michael@0 881 {
michael@0 882 return tan(x);
michael@0 883 }
michael@0 884
michael@0 885 bool
michael@0 886 js::math_tan(JSContext *cx, unsigned argc, Value *vp)
michael@0 887 {
michael@0 888 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 889
michael@0 890 if (args.length() == 0) {
michael@0 891 args.rval().setNaN();
michael@0 892 return true;
michael@0 893 }
michael@0 894
michael@0 895 double x;
michael@0 896 if (!ToNumber(cx, args[0], &x))
michael@0 897 return false;
michael@0 898
michael@0 899 MathCache *mathCache = cx->runtime()->getMathCache(cx);
michael@0 900 if (!mathCache)
michael@0 901 return false;
michael@0 902
michael@0 903 double z = math_tan_impl(mathCache, x);
michael@0 904 args.rval().setDouble(z);
michael@0 905 return true;
michael@0 906 }
michael@0 907
michael@0 908
michael@0 909 typedef double (*UnaryMathFunctionType)(MathCache *cache, double);
michael@0 910
michael@0 911 template <UnaryMathFunctionType F>
michael@0 912 static bool math_function(JSContext *cx, unsigned argc, Value *vp)
michael@0 913 {
michael@0 914 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 915 if (args.length() == 0) {
michael@0 916 args.rval().setNumber(GenericNaN());
michael@0 917 return true;
michael@0 918 }
michael@0 919
michael@0 920 double x;
michael@0 921 if (!ToNumber(cx, args[0], &x))
michael@0 922 return false;
michael@0 923
michael@0 924 MathCache *mathCache = cx->runtime()->getMathCache(cx);
michael@0 925 if (!mathCache)
michael@0 926 return false;
michael@0 927 double z = F(mathCache, x);
michael@0 928 args.rval().setNumber(z);
michael@0 929
michael@0 930 return true;
michael@0 931 }
michael@0 932
michael@0 933
michael@0 934
michael@0 935 double
michael@0 936 js::math_log10_impl(MathCache *cache, double x)
michael@0 937 {
michael@0 938 return cache->lookup(log10, x);
michael@0 939 }
michael@0 940
michael@0 941 double
michael@0 942 js::math_log10_uncached(double x)
michael@0 943 {
michael@0 944 return log10(x);
michael@0 945 }
michael@0 946
michael@0 947 bool
michael@0 948 js::math_log10(JSContext *cx, unsigned argc, Value *vp)
michael@0 949 {
michael@0 950 return math_function<math_log10_impl>(cx, argc, vp);
michael@0 951 }
michael@0 952
michael@0 953 #if !HAVE_LOG2
michael@0 954 double log2(double x)
michael@0 955 {
michael@0 956 return log(x) / M_LN2;
michael@0 957 }
michael@0 958 #endif
michael@0 959
michael@0 960 double
michael@0 961 js::math_log2_impl(MathCache *cache, double x)
michael@0 962 {
michael@0 963 return cache->lookup(log2, x);
michael@0 964 }
michael@0 965
michael@0 966 double
michael@0 967 js::math_log2_uncached(double x)
michael@0 968 {
michael@0 969 return log2(x);
michael@0 970 }
michael@0 971
michael@0 972 bool
michael@0 973 js::math_log2(JSContext *cx, unsigned argc, Value *vp)
michael@0 974 {
michael@0 975 return math_function<math_log2_impl>(cx, argc, vp);
michael@0 976 }
michael@0 977
michael@0 978 #if !HAVE_LOG1P
michael@0 979 double log1p(double x)
michael@0 980 {
michael@0 981 if (fabs(x) < 1e-4) {
michael@0 982 /*
michael@0 983 * Use Taylor approx. log(1 + x) = x - x^2 / 2 + x^3 / 3 - x^4 / 4 with error x^5 / 5
michael@0 984 * Since |x| < 10^-4, |x|^5 < 10^-20, relative error less than 10^-16
michael@0 985 */
michael@0 986 double z = -(x * x * x * x) / 4 + (x * x * x) / 3 - (x * x) / 2 + x;
michael@0 987 return z;
michael@0 988 } else {
michael@0 989 /* For other large enough values of x use direct computation */
michael@0 990 return log(1.0 + x);
michael@0 991 }
michael@0 992 }
michael@0 993 #endif
michael@0 994
michael@0 995 #ifdef __APPLE__
michael@0 996 // Ensure that log1p(-0) is -0.
michael@0 997 #define LOG1P_IF_OUT_OF_RANGE(x) if (x == 0) return x;
michael@0 998 #else
michael@0 999 #define LOG1P_IF_OUT_OF_RANGE(x)
michael@0 1000 #endif
michael@0 1001
michael@0 1002 double
michael@0 1003 js::math_log1p_impl(MathCache *cache, double x)
michael@0 1004 {
michael@0 1005 LOG1P_IF_OUT_OF_RANGE(x);
michael@0 1006 return cache->lookup(log1p, x);
michael@0 1007 }
michael@0 1008
michael@0 1009 double
michael@0 1010 js::math_log1p_uncached(double x)
michael@0 1011 {
michael@0 1012 LOG1P_IF_OUT_OF_RANGE(x);
michael@0 1013 return log1p(x);
michael@0 1014 }
michael@0 1015
michael@0 1016 #undef LOG1P_IF_OUT_OF_RANGE
michael@0 1017
michael@0 1018 bool
michael@0 1019 js::math_log1p(JSContext *cx, unsigned argc, Value *vp)
michael@0 1020 {
michael@0 1021 return math_function<math_log1p_impl>(cx, argc, vp);
michael@0 1022 }
michael@0 1023
michael@0 1024 #if !HAVE_EXPM1
michael@0 1025 double expm1(double x)
michael@0 1026 {
michael@0 1027 /* Special handling for -0 */
michael@0 1028 if (x == 0.0)
michael@0 1029 return x;
michael@0 1030
michael@0 1031 if (fabs(x) < 1e-5) {
michael@0 1032 /*
michael@0 1033 * Use Taylor approx. exp(x) - 1 = x + x^2 / 2 + x^3 / 6 with error x^4 / 24
michael@0 1034 * Since |x| < 10^-5, |x|^4 < 10^-20, relative error less than 10^-15
michael@0 1035 */
michael@0 1036 double z = (x * x * x) / 6 + (x * x) / 2 + x;
michael@0 1037 return z;
michael@0 1038 } else {
michael@0 1039 /* For other large enough values of x use direct computation */
michael@0 1040 return exp(x) - 1.0;
michael@0 1041 }
michael@0 1042 }
michael@0 1043 #endif
michael@0 1044
michael@0 1045 double
michael@0 1046 js::math_expm1_impl(MathCache *cache, double x)
michael@0 1047 {
michael@0 1048 return cache->lookup(expm1, x);
michael@0 1049 }
michael@0 1050
michael@0 1051 double
michael@0 1052 js::math_expm1_uncached(double x)
michael@0 1053 {
michael@0 1054 return expm1(x);
michael@0 1055 }
michael@0 1056
michael@0 1057 bool
michael@0 1058 js::math_expm1(JSContext *cx, unsigned argc, Value *vp)
michael@0 1059 {
michael@0 1060 return math_function<math_expm1_impl>(cx, argc, vp);
michael@0 1061 }
michael@0 1062
michael@0 1063 #if !HAVE_SQRT1PM1
michael@0 1064 /* This algorithm computes sqrt(1+x)-1 for small x */
michael@0 1065 double sqrt1pm1(double x)
michael@0 1066 {
michael@0 1067 if (fabs(x) > 0.75)
michael@0 1068 return sqrt(1 + x) - 1;
michael@0 1069
michael@0 1070 return expm1(log1p(x) / 2);
michael@0 1071 }
michael@0 1072 #endif
michael@0 1073
michael@0 1074
michael@0 1075 double
michael@0 1076 js::math_cosh_impl(MathCache *cache, double x)
michael@0 1077 {
michael@0 1078 return cache->lookup(cosh, x);
michael@0 1079 }
michael@0 1080
michael@0 1081 double
michael@0 1082 js::math_cosh_uncached(double x)
michael@0 1083 {
michael@0 1084 return cosh(x);
michael@0 1085 }
michael@0 1086
michael@0 1087 bool
michael@0 1088 js::math_cosh(JSContext *cx, unsigned argc, Value *vp)
michael@0 1089 {
michael@0 1090 return math_function<math_cosh_impl>(cx, argc, vp);
michael@0 1091 }
michael@0 1092
michael@0 1093 double
michael@0 1094 js::math_sinh_impl(MathCache *cache, double x)
michael@0 1095 {
michael@0 1096 return cache->lookup(sinh, x);
michael@0 1097 }
michael@0 1098
michael@0 1099 double
michael@0 1100 js::math_sinh_uncached(double x)
michael@0 1101 {
michael@0 1102 return sinh(x);
michael@0 1103 }
michael@0 1104
michael@0 1105 bool
michael@0 1106 js::math_sinh(JSContext *cx, unsigned argc, Value *vp)
michael@0 1107 {
michael@0 1108 return math_function<math_sinh_impl>(cx, argc, vp);
michael@0 1109 }
michael@0 1110
michael@0 1111 double
michael@0 1112 js::math_tanh_impl(MathCache *cache, double x)
michael@0 1113 {
michael@0 1114 return cache->lookup(tanh, x);
michael@0 1115 }
michael@0 1116
michael@0 1117 double
michael@0 1118 js::math_tanh_uncached(double x)
michael@0 1119 {
michael@0 1120 return tanh(x);
michael@0 1121 }
michael@0 1122
michael@0 1123 bool
michael@0 1124 js::math_tanh(JSContext *cx, unsigned argc, Value *vp)
michael@0 1125 {
michael@0 1126 return math_function<math_tanh_impl>(cx, argc, vp);
michael@0 1127 }
michael@0 1128
michael@0 1129 #if !HAVE_ACOSH
michael@0 1130 double acosh(double x)
michael@0 1131 {
michael@0 1132 const double SQUARE_ROOT_EPSILON = sqrt(std::numeric_limits<double>::epsilon());
michael@0 1133
michael@0 1134 if ((x - 1) >= SQUARE_ROOT_EPSILON) {
michael@0 1135 if (x > 1 / SQUARE_ROOT_EPSILON) {
michael@0 1136 /*
michael@0 1137 * http://functions.wolfram.com/ElementaryFunctions/ArcCosh/06/01/06/01/0001/
michael@0 1138 * approximation by laurent series in 1/x at 0+ order from -1 to 0
michael@0 1139 */
michael@0 1140 return log(x) + M_LN2;
michael@0 1141 } else if (x < 1.5) {
michael@0 1142 // This is just a rearrangement of the standard form below
michael@0 1143 // devised to minimize loss of precision when x ~ 1:
michael@0 1144 double y = x - 1;
michael@0 1145 return log1p(y + sqrt(y * y + 2 * y));
michael@0 1146 } else {
michael@0 1147 // http://functions.wolfram.com/ElementaryFunctions/ArcCosh/02/
michael@0 1148 return log(x + sqrt(x * x - 1));
michael@0 1149 }
michael@0 1150 } else {
michael@0 1151 // see http://functions.wolfram.com/ElementaryFunctions/ArcCosh/06/01/04/01/0001/
michael@0 1152 double y = x - 1;
michael@0 1153 // approximation by taylor series in y at 0 up to order 2.
michael@0 1154 // If x is less than 1, sqrt(2 * y) is NaN and the result is NaN.
michael@0 1155 return sqrt(2 * y) * (1 - y / 12 + 3 * y * y / 160);
michael@0 1156 }
michael@0 1157 }
michael@0 1158 #endif
michael@0 1159
michael@0 1160 double
michael@0 1161 js::math_acosh_impl(MathCache *cache, double x)
michael@0 1162 {
michael@0 1163 return cache->lookup(acosh, x);
michael@0 1164 }
michael@0 1165
michael@0 1166 double
michael@0 1167 js::math_acosh_uncached(double x)
michael@0 1168 {
michael@0 1169 return acosh(x);
michael@0 1170 }
michael@0 1171
michael@0 1172 bool
michael@0 1173 js::math_acosh(JSContext *cx, unsigned argc, Value *vp)
michael@0 1174 {
michael@0 1175 return math_function<math_acosh_impl>(cx, argc, vp);
michael@0 1176 }
michael@0 1177
michael@0 1178 #if !HAVE_ASINH
michael@0 1179 // Bug 899712 - gcc incorrectly rewrites -asinh(-x) to asinh(x) when overriding
michael@0 1180 // asinh.
michael@0 1181 static double my_asinh(double x)
michael@0 1182 {
michael@0 1183 const double SQUARE_ROOT_EPSILON = sqrt(std::numeric_limits<double>::epsilon());
michael@0 1184 const double FOURTH_ROOT_EPSILON = sqrt(SQUARE_ROOT_EPSILON);
michael@0 1185
michael@0 1186 if (x >= FOURTH_ROOT_EPSILON) {
michael@0 1187 if (x > 1 / SQUARE_ROOT_EPSILON)
michael@0 1188 // http://functions.wolfram.com/ElementaryFunctions/ArcSinh/06/01/06/01/0001/
michael@0 1189 // approximation by laurent series in 1/x at 0+ order from -1 to 1
michael@0 1190 return M_LN2 + log(x) + 1 / (4 * x * x);
michael@0 1191 else if (x < 0.5)
michael@0 1192 return log1p(x + sqrt1pm1(x * x));
michael@0 1193 else
michael@0 1194 return log(x + sqrt(x * x + 1));
michael@0 1195 } else if (x <= -FOURTH_ROOT_EPSILON) {
michael@0 1196 return -my_asinh(-x);
michael@0 1197 } else {
michael@0 1198 // http://functions.wolfram.com/ElementaryFunctions/ArcSinh/06/01/03/01/0001/
michael@0 1199 // approximation by taylor series in x at 0 up to order 2
michael@0 1200 double result = x;
michael@0 1201
michael@0 1202 if (fabs(x) >= SQUARE_ROOT_EPSILON) {
michael@0 1203 double x3 = x * x * x;
michael@0 1204 // approximation by taylor series in x at 0 up to order 4
michael@0 1205 result -= x3 / 6;
michael@0 1206 }
michael@0 1207
michael@0 1208 return result;
michael@0 1209 }
michael@0 1210 }
michael@0 1211 #endif
michael@0 1212
michael@0 1213 double
michael@0 1214 js::math_asinh_impl(MathCache *cache, double x)
michael@0 1215 {
michael@0 1216 #ifdef HAVE_ASINH
michael@0 1217 return cache->lookup(asinh, x);
michael@0 1218 #else
michael@0 1219 return cache->lookup(my_asinh, x);
michael@0 1220 #endif
michael@0 1221 }
michael@0 1222
michael@0 1223 double
michael@0 1224 js::math_asinh_uncached(double x)
michael@0 1225 {
michael@0 1226 #ifdef HAVE_ASINH
michael@0 1227 return asinh(x);
michael@0 1228 #else
michael@0 1229 return my_asinh(x);
michael@0 1230 #endif
michael@0 1231 }
michael@0 1232
michael@0 1233 bool
michael@0 1234 js::math_asinh(JSContext *cx, unsigned argc, Value *vp)
michael@0 1235 {
michael@0 1236 return math_function<math_asinh_impl>(cx, argc, vp);
michael@0 1237 }
michael@0 1238
michael@0 1239 #if !HAVE_ATANH
michael@0 1240 double atanh(double x)
michael@0 1241 {
michael@0 1242 const double EPSILON = std::numeric_limits<double>::epsilon();
michael@0 1243 const double SQUARE_ROOT_EPSILON = sqrt(EPSILON);
michael@0 1244 const double FOURTH_ROOT_EPSILON = sqrt(SQUARE_ROOT_EPSILON);
michael@0 1245
michael@0 1246 if (fabs(x) >= FOURTH_ROOT_EPSILON) {
michael@0 1247 // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/02/
michael@0 1248 if (fabs(x) < 0.5)
michael@0 1249 return (log1p(x) - log1p(-x)) / 2;
michael@0 1250
michael@0 1251 return log((1 + x) / (1 - x)) / 2;
michael@0 1252 } else {
michael@0 1253 // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/06/01/03/01/
michael@0 1254 // approximation by taylor series in x at 0 up to order 2
michael@0 1255 double result = x;
michael@0 1256
michael@0 1257 if (fabs(x) >= SQUARE_ROOT_EPSILON) {
michael@0 1258 double x3 = x * x * x;
michael@0 1259 result += x3 / 3;
michael@0 1260 }
michael@0 1261
michael@0 1262 return result;
michael@0 1263 }
michael@0 1264 }
michael@0 1265 #endif
michael@0 1266
michael@0 1267 double
michael@0 1268 js::math_atanh_impl(MathCache *cache, double x)
michael@0 1269 {
michael@0 1270 return cache->lookup(atanh, x);
michael@0 1271 }
michael@0 1272
michael@0 1273 double
michael@0 1274 js::math_atanh_uncached(double x)
michael@0 1275 {
michael@0 1276 return atanh(x);
michael@0 1277 }
michael@0 1278
michael@0 1279 bool
michael@0 1280 js::math_atanh(JSContext *cx, unsigned argc, Value *vp)
michael@0 1281 {
michael@0 1282 return math_function<math_atanh_impl>(cx, argc, vp);
michael@0 1283 }
michael@0 1284
michael@0 1285 /* Consistency wrapper for platform deviations in hypot() */
michael@0 1286 double
michael@0 1287 js::ecmaHypot(double x, double y)
michael@0 1288 {
michael@0 1289 #ifdef XP_WIN
michael@0 1290 /*
michael@0 1291 * Workaround MS hypot bug, where hypot(Infinity, NaN or Math.MIN_VALUE)
michael@0 1292 * is NaN, not Infinity.
michael@0 1293 */
michael@0 1294 if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y)) {
michael@0 1295 return mozilla::PositiveInfinity<double>();
michael@0 1296 }
michael@0 1297 #endif
michael@0 1298 return hypot(x, y);
michael@0 1299 }
michael@0 1300
michael@0 1301 bool
michael@0 1302 js::math_hypot(JSContext *cx, unsigned argc, Value *vp)
michael@0 1303 {
michael@0 1304 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 1305
michael@0 1306 // IonMonkey calls the system hypot function directly if two arguments are
michael@0 1307 // given. Do that here as well to get the same results.
michael@0 1308 if (args.length() == 2) {
michael@0 1309 double x, y;
michael@0 1310 if (!ToNumber(cx, args[0], &x))
michael@0 1311 return false;
michael@0 1312 if (!ToNumber(cx, args[1], &y))
michael@0 1313 return false;
michael@0 1314
michael@0 1315 double result = ecmaHypot(x, y);
michael@0 1316 args.rval().setNumber(result);
michael@0 1317 return true;
michael@0 1318 }
michael@0 1319
michael@0 1320 bool isInfinite = false;
michael@0 1321 bool isNaN = false;
michael@0 1322
michael@0 1323 double scale = 0;
michael@0 1324 double sumsq = 1;
michael@0 1325
michael@0 1326 for (unsigned i = 0; i < args.length(); i++) {
michael@0 1327 double x;
michael@0 1328 if (!ToNumber(cx, args[i], &x))
michael@0 1329 return false;
michael@0 1330
michael@0 1331 isInfinite |= mozilla::IsInfinite(x);
michael@0 1332 isNaN |= mozilla::IsNaN(x);
michael@0 1333
michael@0 1334 double xabs = mozilla::Abs(x);
michael@0 1335
michael@0 1336 if (scale < xabs) {
michael@0 1337 sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
michael@0 1338 scale = xabs;
michael@0 1339 } else if (scale != 0) {
michael@0 1340 sumsq += (xabs / scale) * (xabs / scale);
michael@0 1341 }
michael@0 1342 }
michael@0 1343
michael@0 1344 double result = isInfinite ? PositiveInfinity<double>() :
michael@0 1345 isNaN ? GenericNaN() :
michael@0 1346 scale * sqrt(sumsq);
michael@0 1347 args.rval().setNumber(result);
michael@0 1348 return true;
michael@0 1349 }
michael@0 1350
michael@0 1351 #if !HAVE_TRUNC
michael@0 1352 double trunc(double x)
michael@0 1353 {
michael@0 1354 return x > 0 ? floor(x) : ceil(x);
michael@0 1355 }
michael@0 1356 #endif
michael@0 1357
michael@0 1358 double
michael@0 1359 js::math_trunc_impl(MathCache *cache, double x)
michael@0 1360 {
michael@0 1361 return cache->lookup(trunc, x);
michael@0 1362 }
michael@0 1363
michael@0 1364 double
michael@0 1365 js::math_trunc_uncached(double x)
michael@0 1366 {
michael@0 1367 return trunc(x);
michael@0 1368 }
michael@0 1369
michael@0 1370 bool
michael@0 1371 js::math_trunc(JSContext *cx, unsigned argc, Value *vp)
michael@0 1372 {
michael@0 1373 return math_function<math_trunc_impl>(cx, argc, vp);
michael@0 1374 }
michael@0 1375
michael@0 1376 static double sign(double x)
michael@0 1377 {
michael@0 1378 if (mozilla::IsNaN(x))
michael@0 1379 return GenericNaN();
michael@0 1380
michael@0 1381 return x == 0 ? x : x < 0 ? -1 : 1;
michael@0 1382 }
michael@0 1383
michael@0 1384 double
michael@0 1385 js::math_sign_impl(MathCache *cache, double x)
michael@0 1386 {
michael@0 1387 return cache->lookup(sign, x);
michael@0 1388 }
michael@0 1389
michael@0 1390 double
michael@0 1391 js::math_sign_uncached(double x)
michael@0 1392 {
michael@0 1393 return sign(x);
michael@0 1394 }
michael@0 1395
michael@0 1396 bool
michael@0 1397 js::math_sign(JSContext *cx, unsigned argc, Value *vp)
michael@0 1398 {
michael@0 1399 return math_function<math_sign_impl>(cx, argc, vp);
michael@0 1400 }
michael@0 1401
michael@0 1402 #if !HAVE_CBRT
michael@0 1403 double cbrt(double x)
michael@0 1404 {
michael@0 1405 if (x > 0) {
michael@0 1406 return pow(x, 1.0 / 3.0);
michael@0 1407 } else if (x == 0) {
michael@0 1408 return x;
michael@0 1409 } else {
michael@0 1410 return -pow(-x, 1.0 / 3.0);
michael@0 1411 }
michael@0 1412 }
michael@0 1413 #endif
michael@0 1414
michael@0 1415 double
michael@0 1416 js::math_cbrt_impl(MathCache *cache, double x)
michael@0 1417 {
michael@0 1418 return cache->lookup(cbrt, x);
michael@0 1419 }
michael@0 1420
michael@0 1421 double
michael@0 1422 js::math_cbrt_uncached(double x)
michael@0 1423 {
michael@0 1424 return cbrt(x);
michael@0 1425 }
michael@0 1426
michael@0 1427 bool
michael@0 1428 js::math_cbrt(JSContext *cx, unsigned argc, Value *vp)
michael@0 1429 {
michael@0 1430 return math_function<math_cbrt_impl>(cx, argc, vp);
michael@0 1431 }
michael@0 1432
michael@0 1433 #if JS_HAS_TOSOURCE
michael@0 1434 static bool
michael@0 1435 math_toSource(JSContext *cx, unsigned argc, Value *vp)
michael@0 1436 {
michael@0 1437 CallArgs args = CallArgsFromVp(argc, vp);
michael@0 1438 args.rval().setString(cx->names().Math);
michael@0 1439 return true;
michael@0 1440 }
michael@0 1441 #endif
michael@0 1442
michael@0 1443 static const JSFunctionSpec math_static_methods[] = {
michael@0 1444 #if JS_HAS_TOSOURCE
michael@0 1445 JS_FN(js_toSource_str, math_toSource, 0, 0),
michael@0 1446 #endif
michael@0 1447 JS_FN("abs", js_math_abs, 1, 0),
michael@0 1448 JS_FN("acos", math_acos, 1, 0),
michael@0 1449 JS_FN("asin", math_asin, 1, 0),
michael@0 1450 JS_FN("atan", math_atan, 1, 0),
michael@0 1451 JS_FN("atan2", math_atan2, 2, 0),
michael@0 1452 JS_FN("ceil", math_ceil, 1, 0),
michael@0 1453 JS_FN("clz32", math_clz32, 1, 0),
michael@0 1454 JS_FN("cos", math_cos, 1, 0),
michael@0 1455 JS_FN("exp", math_exp, 1, 0),
michael@0 1456 JS_FN("floor", math_floor, 1, 0),
michael@0 1457 JS_FN("imul", math_imul, 2, 0),
michael@0 1458 JS_FN("fround", math_fround, 1, 0),
michael@0 1459 JS_FN("log", math_log, 1, 0),
michael@0 1460 JS_FN("max", js_math_max, 2, 0),
michael@0 1461 JS_FN("min", js_math_min, 2, 0),
michael@0 1462 JS_FN("pow", js_math_pow, 2, 0),
michael@0 1463 JS_FN("random", js_math_random, 0, 0),
michael@0 1464 JS_FN("round", math_round, 1, 0),
michael@0 1465 JS_FN("sin", math_sin, 1, 0),
michael@0 1466 JS_FN("sqrt", js_math_sqrt, 1, 0),
michael@0 1467 JS_FN("tan", math_tan, 1, 0),
michael@0 1468 JS_FN("log10", math_log10, 1, 0),
michael@0 1469 JS_FN("log2", math_log2, 1, 0),
michael@0 1470 JS_FN("log1p", math_log1p, 1, 0),
michael@0 1471 JS_FN("expm1", math_expm1, 1, 0),
michael@0 1472 JS_FN("cosh", math_cosh, 1, 0),
michael@0 1473 JS_FN("sinh", math_sinh, 1, 0),
michael@0 1474 JS_FN("tanh", math_tanh, 1, 0),
michael@0 1475 JS_FN("acosh", math_acosh, 1, 0),
michael@0 1476 JS_FN("asinh", math_asinh, 1, 0),
michael@0 1477 JS_FN("atanh", math_atanh, 1, 0),
michael@0 1478 JS_FN("hypot", math_hypot, 2, 0),
michael@0 1479 JS_FN("trunc", math_trunc, 1, 0),
michael@0 1480 JS_FN("sign", math_sign, 1, 0),
michael@0 1481 JS_FN("cbrt", math_cbrt, 1, 0),
michael@0 1482 JS_FS_END
michael@0 1483 };
michael@0 1484
michael@0 1485 JSObject *
michael@0 1486 js_InitMathClass(JSContext *cx, HandleObject obj)
michael@0 1487 {
michael@0 1488 RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx));
michael@0 1489 if (!proto)
michael@0 1490 return nullptr;
michael@0 1491 RootedObject Math(cx, NewObjectWithGivenProto(cx, &MathClass, proto, obj, SingletonObject));
michael@0 1492 if (!Math)
michael@0 1493 return nullptr;
michael@0 1494
michael@0 1495 if (!JS_DefineProperty(cx, obj, js_Math_str, Math, 0,
michael@0 1496 JS_PropertyStub, JS_StrictPropertyStub))
michael@0 1497 {
michael@0 1498 return nullptr;
michael@0 1499 }
michael@0 1500
michael@0 1501 if (!JS_DefineFunctions(cx, Math, math_static_methods))
michael@0 1502 return nullptr;
michael@0 1503 if (!JS_DefineConstDoubles(cx, Math, math_constants))
michael@0 1504 return nullptr;
michael@0 1505
michael@0 1506 obj->as<GlobalObject>().setConstructor(JSProto_Math, ObjectValue(*Math));
michael@0 1507
michael@0 1508 return Math;
michael@0 1509 }

mercurial