Bumping gaia.json for 1 gaia revision(s) a=gaia-bump
[gecko.git] / js / src / jsmath.cpp
blob639ef794dfe9a238bc3082e220719d516cda501c
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * JS math package.
9 */
11 #include "jsmath.h"
13 #include "mozilla/Constants.h"
14 #include "mozilla/FloatingPoint.h"
15 #include "mozilla/MathAlgorithms.h"
16 #include "mozilla/MemoryReporting.h"
18 #include <algorithm> // for std::max
19 #include <fcntl.h>
21 #ifdef XP_UNIX
22 # include <unistd.h>
23 #endif
25 #include "jsapi.h"
26 #include "jsatom.h"
27 #include "jscntxt.h"
28 #include "jscompartment.h"
29 #include "jslibmath.h"
30 #include "jstypes.h"
31 #include "prmjtime.h"
33 #include "jsobjinlines.h"
35 using namespace js;
37 using mozilla::Abs;
38 using mozilla::NumberEqualsInt32;
39 using mozilla::NumberIsInt32;
40 using mozilla::ExponentComponent;
41 using mozilla::FloatingPoint;
42 using mozilla::IsFinite;
43 using mozilla::IsInfinite;
44 using mozilla::IsNaN;
45 using mozilla::IsNegative;
46 using mozilla::IsNegativeZero;
47 using mozilla::PositiveInfinity;
48 using mozilla::NegativeInfinity;
49 using JS::ToNumber;
50 using JS::GenericNaN;
52 static const JSConstDoubleSpec math_constants[] = {
53 {"E" , M_E },
54 {"LOG2E" , M_LOG2E },
55 {"LOG10E" , M_LOG10E },
56 {"LN2" , M_LN2 },
57 {"LN10" , M_LN10 },
58 {"PI" , M_PI },
59 {"SQRT2" , M_SQRT2 },
60 {"SQRT1_2", M_SQRT1_2 },
61 {0,0}
64 MathCache::MathCache() {
65 memset(table, 0, sizeof(table));
67 /* See comments in lookup(). */
68 MOZ_ASSERT(IsNegativeZero(-0.0));
69 MOZ_ASSERT(!IsNegativeZero(+0.0));
70 MOZ_ASSERT(hash(-0.0, MathCache::Sin) != hash(+0.0, MathCache::Sin));
73 size_t
74 MathCache::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
76 return mallocSizeOf(this);
79 const Class js::MathClass = {
80 js_Math_str,
81 JSCLASS_HAS_CACHED_PROTO(JSProto_Math)
84 bool
85 js::math_abs_handle(JSContext* cx, js::HandleValue v, js::MutableHandleValue r)
87 double x;
88 if (!ToNumber(cx, v, &x))
89 return false;
91 double z = Abs(x);
92 r.setNumber(z);
94 return true;
97 bool
98 js::math_abs(JSContext* cx, unsigned argc, Value* vp)
100 CallArgs args = CallArgsFromVp(argc, vp);
102 if (args.length() == 0) {
103 args.rval().setNaN();
104 return true;
107 return math_abs_handle(cx, args[0], args.rval());
110 #if defined(SOLARIS) && defined(__GNUC__)
111 #define ACOS_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return GenericNaN();
112 #else
113 #define ACOS_IF_OUT_OF_RANGE(x)
114 #endif
116 double
117 js::math_acos_impl(MathCache* cache, double x)
119 ACOS_IF_OUT_OF_RANGE(x);
120 return cache->lookup(acos, x, MathCache::Acos);
123 double
124 js::math_acos_uncached(double x)
126 ACOS_IF_OUT_OF_RANGE(x);
127 return acos(x);
130 #undef ACOS_IF_OUT_OF_RANGE
132 bool
133 js::math_acos(JSContext* cx, unsigned argc, Value* vp)
135 CallArgs args = CallArgsFromVp(argc, vp);
137 if (args.length() == 0) {
138 args.rval().setNaN();
139 return true;
142 double x;
143 if (!ToNumber(cx, args[0], &x))
144 return false;
146 MathCache* mathCache = cx->runtime()->getMathCache(cx);
147 if (!mathCache)
148 return false;
150 double z = math_acos_impl(mathCache, x);
151 args.rval().setDouble(z);
152 return true;
155 #if defined(SOLARIS) && defined(__GNUC__)
156 #define ASIN_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return GenericNaN();
157 #else
158 #define ASIN_IF_OUT_OF_RANGE(x)
159 #endif
161 double
162 js::math_asin_impl(MathCache* cache, double x)
164 ASIN_IF_OUT_OF_RANGE(x);
165 return cache->lookup(asin, x, MathCache::Asin);
168 double
169 js::math_asin_uncached(double x)
171 ASIN_IF_OUT_OF_RANGE(x);
172 return asin(x);
175 #undef ASIN_IF_OUT_OF_RANGE
177 bool
178 js::math_asin(JSContext* cx, unsigned argc, Value* vp)
180 CallArgs args = CallArgsFromVp(argc, vp);
182 if (args.length() == 0) {
183 args.rval().setNaN();
184 return true;
187 double x;
188 if (!ToNumber(cx, args[0], &x))
189 return false;
191 MathCache* mathCache = cx->runtime()->getMathCache(cx);
192 if (!mathCache)
193 return false;
195 double z = math_asin_impl(mathCache, x);
196 args.rval().setDouble(z);
197 return true;
200 double
201 js::math_atan_impl(MathCache* cache, double x)
203 return cache->lookup(atan, x, MathCache::Atan);
206 double
207 js::math_atan_uncached(double x)
209 return atan(x);
212 bool
213 js::math_atan(JSContext* cx, unsigned argc, Value* vp)
215 CallArgs args = CallArgsFromVp(argc, vp);
217 if (args.length() == 0) {
218 args.rval().setNaN();
219 return true;
222 double x;
223 if (!ToNumber(cx, args[0], &x))
224 return false;
226 MathCache* mathCache = cx->runtime()->getMathCache(cx);
227 if (!mathCache)
228 return false;
230 double z = math_atan_impl(mathCache, x);
231 args.rval().setDouble(z);
232 return true;
235 double
236 js::ecmaAtan2(double y, double x)
238 #if defined(_MSC_VER)
240 * MSVC's atan2 does not yield the result demanded by ECMA when both x
241 * and y are infinite.
242 * - The result is a multiple of pi/4.
243 * - The sign of y determines the sign of the result.
244 * - The sign of x determines the multiplicator, 1 or 3.
246 if (IsInfinite(y) && IsInfinite(x)) {
247 double z = js_copysign(M_PI / 4, y);
248 if (x < 0)
249 z *= 3;
250 return z;
252 #endif
254 #if defined(SOLARIS) && defined(__GNUC__)
255 if (y == 0) {
256 if (IsNegativeZero(x))
257 return js_copysign(M_PI, y);
258 if (x == 0)
259 return y;
261 #endif
262 return atan2(y, x);
265 bool
266 js::math_atan2_handle(JSContext* cx, HandleValue y, HandleValue x, MutableHandleValue res)
268 double dy;
269 if (!ToNumber(cx, y, &dy))
270 return false;
272 double dx;
273 if (!ToNumber(cx, x, &dx))
274 return false;
276 double z = ecmaAtan2(dy, dx);
277 res.setDouble(z);
278 return true;
281 bool
282 js::math_atan2(JSContext* cx, unsigned argc, Value* vp)
284 CallArgs args = CallArgsFromVp(argc, vp);
286 return math_atan2_handle(cx, args.get(0), args.get(1), args.rval());
289 double
290 js::math_ceil_impl(double x)
292 #ifdef __APPLE__
293 if (x < 0 && x > -1.0)
294 return js_copysign(0, -1);
295 #endif
296 return ceil(x);
299 bool
300 js::math_ceil(JSContext* cx, unsigned argc, Value* vp)
302 CallArgs args = CallArgsFromVp(argc, vp);
304 if (args.length() == 0) {
305 args.rval().setNaN();
306 return true;
309 double x;
310 if (!ToNumber(cx, args[0], &x))
311 return false;
313 double z = math_ceil_impl(x);
314 args.rval().setNumber(z);
315 return true;
318 bool
319 js::math_clz32(JSContext* cx, unsigned argc, Value* vp)
321 CallArgs args = CallArgsFromVp(argc, vp);
323 if (args.length() == 0) {
324 args.rval().setInt32(32);
325 return true;
328 uint32_t n;
329 if (!ToUint32(cx, args[0], &n))
330 return false;
332 if (n == 0) {
333 args.rval().setInt32(32);
334 return true;
337 args.rval().setInt32(mozilla::CountLeadingZeroes32(n));
338 return true;
341 double
342 js::math_cos_impl(MathCache* cache, double x)
344 return cache->lookup(cos, x, MathCache::Cos);
347 double
348 js::math_cos_uncached(double x)
350 return cos(x);
353 bool
354 js::math_cos(JSContext* cx, unsigned argc, Value* vp)
356 CallArgs args = CallArgsFromVp(argc, vp);
358 if (args.length() == 0) {
359 args.rval().setNaN();
360 return true;
363 double x;
364 if (!ToNumber(cx, args[0], &x))
365 return false;
367 MathCache* mathCache = cx->runtime()->getMathCache(cx);
368 if (!mathCache)
369 return false;
371 double z = math_cos_impl(mathCache, x);
372 args.rval().setDouble(z);
373 return true;
376 #ifdef _WIN32
377 #define EXP_IF_OUT_OF_RANGE(x) \
378 if (!IsNaN(x)) { \
379 if (x == PositiveInfinity<double>()) \
380 return PositiveInfinity<double>(); \
381 if (x == NegativeInfinity<double>()) \
382 return 0.0; \
384 #else
385 #define EXP_IF_OUT_OF_RANGE(x)
386 #endif
388 double
389 js::math_exp_impl(MathCache* cache, double x)
391 EXP_IF_OUT_OF_RANGE(x);
392 return cache->lookup(exp, x, MathCache::Exp);
395 double
396 js::math_exp_uncached(double x)
398 EXP_IF_OUT_OF_RANGE(x);
399 return exp(x);
402 #undef EXP_IF_OUT_OF_RANGE
404 bool
405 js::math_exp(JSContext* cx, unsigned argc, Value* vp)
407 CallArgs args = CallArgsFromVp(argc, vp);
409 if (args.length() == 0) {
410 args.rval().setNaN();
411 return true;
414 double x;
415 if (!ToNumber(cx, args[0], &x))
416 return false;
418 MathCache* mathCache = cx->runtime()->getMathCache(cx);
419 if (!mathCache)
420 return false;
422 double z = math_exp_impl(mathCache, x);
423 args.rval().setNumber(z);
424 return true;
427 double
428 js::math_floor_impl(double x)
430 return floor(x);
433 bool
434 js::math_floor_handle(JSContext* cx, HandleValue v, MutableHandleValue r)
436 double d;
437 if(!ToNumber(cx, v, &d))
438 return false;
440 double z = math_floor_impl(d);
441 r.setNumber(z);
443 return true;
446 bool
447 js::math_floor(JSContext* cx, unsigned argc, Value* vp)
449 CallArgs args = CallArgsFromVp(argc, vp);
451 if (args.length() == 0) {
452 args.rval().setNaN();
453 return true;
456 return math_floor_handle(cx, args[0], args.rval());
459 bool
460 js::math_imul(JSContext* cx, unsigned argc, Value* vp)
462 CallArgs args = CallArgsFromVp(argc, vp);
464 uint32_t a = 0, b = 0;
465 if (args.hasDefined(0) && !ToUint32(cx, args[0], &a))
466 return false;
467 if (args.hasDefined(1) && !ToUint32(cx, args[1], &b))
468 return false;
470 uint32_t product = a * b;
471 args.rval().setInt32(product > INT32_MAX
472 ? int32_t(INT32_MIN + (product - INT32_MAX - 1))
473 : int32_t(product));
474 return true;
477 // Implements Math.fround (20.2.2.16) up to step 3
478 bool
479 js::RoundFloat32(JSContext* cx, HandleValue v, float* out)
481 double d;
482 bool success = ToNumber(cx, v, &d);
483 *out = static_cast<float>(d);
484 return success;
487 bool
488 js::RoundFloat32(JSContext* cx, HandleValue arg, MutableHandleValue res)
490 float f;
491 if (!RoundFloat32(cx, arg, &f))
492 return false;
494 res.setDouble(static_cast<double>(f));
495 return true;
498 bool
499 js::math_fround(JSContext* cx, unsigned argc, Value* vp)
501 CallArgs args = CallArgsFromVp(argc, vp);
503 if (args.length() == 0) {
504 args.rval().setNaN();
505 return true;
508 float f;
509 if (!RoundFloat32(cx, args[0], &f))
510 return false;
512 args.rval().setDouble(static_cast<double>(f));
513 return true;
516 #if defined(SOLARIS) && defined(__GNUC__)
517 #define LOG_IF_OUT_OF_RANGE(x) if (x < 0) return GenericNaN();
518 #else
519 #define LOG_IF_OUT_OF_RANGE(x)
520 #endif
522 double
523 js::math_log_impl(MathCache* cache, double x)
525 LOG_IF_OUT_OF_RANGE(x);
526 return cache->lookup(log, x, MathCache::Log);
529 double
530 js::math_log_uncached(double x)
532 LOG_IF_OUT_OF_RANGE(x);
533 return log(x);
536 #undef LOG_IF_OUT_OF_RANGE
538 bool
539 js::math_log(JSContext* cx, unsigned argc, Value* vp)
541 CallArgs args = CallArgsFromVp(argc, vp);
543 if (args.length() == 0) {
544 args.rval().setNaN();
545 return true;
548 double x;
549 if (!ToNumber(cx, args[0], &x))
550 return false;
552 MathCache* mathCache = cx->runtime()->getMathCache(cx);
553 if (!mathCache)
554 return false;
556 double z = math_log_impl(mathCache, x);
557 args.rval().setNumber(z);
558 return true;
561 double
562 js::math_max_impl(double x, double y)
564 // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
565 if (x > y || IsNaN(x) || (x == y && IsNegative(y)))
566 return x;
567 return y;
570 bool
571 js::math_max(JSContext* cx, unsigned argc, Value* vp)
573 CallArgs args = CallArgsFromVp(argc, vp);
575 double maxval = NegativeInfinity<double>();
576 for (unsigned i = 0; i < args.length(); i++) {
577 double x;
578 if (!ToNumber(cx, args[i], &x))
579 return false;
580 maxval = math_max_impl(x, maxval);
582 args.rval().setNumber(maxval);
583 return true;
586 double
587 js::math_min_impl(double x, double y)
589 // Math.min(num, NaN) => NaN, Math.min(-0, +0) => -0
590 if (x < y || IsNaN(x) || (x == y && IsNegativeZero(x)))
591 return x;
592 return y;
595 bool
596 js::math_min(JSContext* cx, unsigned argc, Value* vp)
598 CallArgs args = CallArgsFromVp(argc, vp);
600 double minval = PositiveInfinity<double>();
601 for (unsigned i = 0; i < args.length(); i++) {
602 double x;
603 if (!ToNumber(cx, args[i], &x))
604 return false;
605 minval = math_min_impl(x, minval);
607 args.rval().setNumber(minval);
608 return true;
611 bool
612 js::minmax_impl(JSContext* cx, bool max, HandleValue a, HandleValue b, MutableHandleValue res)
614 double x, y;
616 if (!ToNumber(cx, a, &x))
617 return false;
618 if (!ToNumber(cx, b, &y))
619 return false;
621 if (max)
622 res.setNumber(math_max_impl(x, y));
623 else
624 res.setNumber(math_min_impl(x, y));
626 return true;
629 double
630 js::powi(double x, int y)
632 unsigned n = (y < 0) ? -y : y;
633 double m = x;
634 double p = 1;
635 while (true) {
636 if ((n & 1) != 0) p *= m;
637 n >>= 1;
638 if (n == 0) {
639 if (y < 0) {
640 // Unfortunately, we have to be careful when p has reached
641 // infinity in the computation, because sometimes the higher
642 // internal precision in the pow() implementation would have
643 // given us a finite p. This happens very rarely.
645 double result = 1.0 / p;
646 return (result == 0 && IsInfinite(p))
647 ? pow(x, static_cast<double>(y)) // Avoid pow(double, int).
648 : result;
651 return p;
653 m *= m;
657 double
658 js::ecmaPow(double x, double y)
661 * Use powi if the exponent is an integer-valued double. We don't have to
662 * check for NaN since a comparison with NaN is always false.
664 int32_t yi;
665 if (NumberEqualsInt32(y, &yi))
666 return powi(x, yi);
669 * Because C99 and ECMA specify different behavior for pow(),
670 * we need to wrap the libm call to make it ECMA compliant.
672 if (!IsFinite(y) && (x == 1.0 || x == -1.0))
673 return GenericNaN();
675 /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */
676 if (y == 0)
677 return 1;
680 * Special case for square roots. Note that pow(x, 0.5) != sqrt(x)
681 * when x = -0.0, so we have to guard for this.
683 if (IsFinite(x) && x != 0.0) {
684 if (y == 0.5)
685 return sqrt(x);
686 if (y == -0.5)
687 return 1.0 / sqrt(x);
689 return pow(x, y);
692 bool
693 js::math_pow_handle(JSContext* cx, HandleValue base, HandleValue power, MutableHandleValue result)
695 double x;
696 if (!ToNumber(cx, base, &x))
697 return false;
699 double y;
700 if (!ToNumber(cx, power, &y))
701 return false;
703 double z = ecmaPow(x, y);
704 result.setNumber(z);
705 return true;
708 bool
709 js::math_pow(JSContext* cx, unsigned argc, Value* vp)
711 CallArgs args = CallArgsFromVp(argc, vp);
713 return math_pow_handle(cx, args.get(0), args.get(1), args.rval());
716 static uint64_t
717 random_generateSeed()
719 union {
720 uint8_t u8[8];
721 uint32_t u32[2];
722 uint64_t u64;
723 } seed;
724 seed.u64 = 0;
726 #if defined(XP_WIN)
728 * Our PRNG only uses 48 bits, so calling rand_s() twice to get 64 bits is
729 * probably overkill.
731 rand_s(&seed.u32[0]);
732 #elif defined(XP_UNIX)
734 * In the unlikely event we can't read /dev/urandom, there's not much we can
735 * do, so just mix in the fd error code and the current time.
737 int fd = open("/dev/urandom", O_RDONLY);
738 MOZ_ASSERT(fd >= 0, "Can't open /dev/urandom");
739 if (fd >= 0) {
740 (void)read(fd, seed.u8, mozilla::ArrayLength(seed.u8));
741 close(fd);
743 seed.u32[0] ^= fd;
744 #else
745 # error "Platform needs to implement random_generateSeed()"
746 #endif
748 seed.u32[1] ^= PRMJ_Now();
749 return seed.u64;
752 static const uint64_t RNG_MULTIPLIER = 0x5DEECE66DLL;
753 static const uint64_t RNG_ADDEND = 0xBLL;
754 static const uint64_t RNG_MASK = (1LL << 48) - 1;
757 * Math.random() support, lifted from java.util.Random.java.
759 void
760 js::random_initState(uint64_t* rngState)
762 /* Our PRNG only uses 48 bits, so squeeze our entropy into those bits. */
763 uint64_t seed = random_generateSeed();
764 seed ^= (seed >> 16);
765 *rngState = (seed ^ RNG_MULTIPLIER) & RNG_MASK;
768 uint64_t
769 js::random_next(uint64_t* rngState, int bits)
771 MOZ_ASSERT((*rngState & 0xffff000000000000ULL) == 0, "Bad rngState");
772 MOZ_ASSERT(bits > 0 && bits <= 48, "bits is out of range");
774 if (*rngState == 0) {
775 random_initState(rngState);
778 uint64_t nextstate = *rngState * RNG_MULTIPLIER;
779 nextstate += RNG_ADDEND;
780 nextstate &= RNG_MASK;
781 *rngState = nextstate;
782 return nextstate >> (48 - bits);
785 double
786 js::math_random_no_outparam(JSContext* cx)
788 /* Calculate random without memory traffic, for use in the JITs. */
789 return random_nextDouble(&cx->compartment()->rngState);
792 bool
793 js::math_random(JSContext* cx, unsigned argc, Value* vp)
795 CallArgs args = CallArgsFromVp(argc, vp);
796 double z = random_nextDouble(&cx->compartment()->rngState);
797 args.rval().setDouble(z);
798 return true;
801 bool
802 js::math_round_handle(JSContext* cx, HandleValue arg, MutableHandleValue res)
804 double d;
805 if (!ToNumber(cx, arg, &d))
806 return false;
808 d = math_round_impl(d);
809 res.setNumber(d);
810 return true;
813 template<typename T>
815 js::GetBiggestNumberLessThan(T x)
817 MOZ_ASSERT(!IsNegative(x));
818 MOZ_ASSERT(IsFinite(x));
819 typedef typename mozilla::FloatingPoint<T>::Bits Bits;
820 Bits bits = mozilla::BitwiseCast<Bits>(x);
821 MOZ_ASSERT(bits > 0, "will underflow");
822 return mozilla::BitwiseCast<T>(bits - 1);
825 template double js::GetBiggestNumberLessThan<>(double x);
826 template float js::GetBiggestNumberLessThan<>(float x);
828 double
829 js::math_round_impl(double x)
831 int32_t ignored;
832 if (NumberIsInt32(x, &ignored))
833 return x;
835 /* Some numbers are so big that adding 0.5 would give the wrong number. */
836 if (ExponentComponent(x) >= int_fast16_t(FloatingPoint<double>::kExponentShift))
837 return x;
839 double add = (x >= 0) ? GetBiggestNumberLessThan(0.5) : 0.5;
840 return js_copysign(floor(x + add), x);
843 float
844 js::math_roundf_impl(float x)
846 int32_t ignored;
847 if (NumberIsInt32(x, &ignored))
848 return x;
850 /* Some numbers are so big that adding 0.5 would give the wrong number. */
851 if (ExponentComponent(x) >= int_fast16_t(FloatingPoint<float>::kExponentShift))
852 return x;
854 float add = (x >= 0) ? GetBiggestNumberLessThan(0.5f) : 0.5f;
855 return js_copysign(floorf(x + add), x);
858 bool /* ES5 15.8.2.15. */
859 js::math_round(JSContext* cx, unsigned argc, Value* vp)
861 CallArgs args = CallArgsFromVp(argc, vp);
863 if (args.length() == 0) {
864 args.rval().setNaN();
865 return true;
868 return math_round_handle(cx, args[0], args.rval());
871 double
872 js::math_sin_impl(MathCache* cache, double x)
874 return cache->lookup(math_sin_uncached, x, MathCache::Sin);
877 double
878 js::math_sin_uncached(double x)
880 #ifdef _WIN64
881 // Workaround MSVC bug where sin(-0) is +0 instead of -0 on x64 on
882 // CPUs without FMA3 (pre-Haswell). See bug 1076670.
883 if (IsNegativeZero(x))
884 return -0.0;
885 #endif
886 return sin(x);
889 bool
890 js::math_sin_handle(JSContext* cx, HandleValue val, MutableHandleValue res)
892 double in;
893 if (!ToNumber(cx, val, &in))
894 return false;
896 MathCache* mathCache = cx->runtime()->getMathCache(cx);
897 if (!mathCache)
898 return false;
900 double out = math_sin_impl(mathCache, in);
901 res.setDouble(out);
902 return true;
905 bool
906 js::math_sin(JSContext* cx, unsigned argc, Value* vp)
908 CallArgs args = CallArgsFromVp(argc, vp);
910 if (args.length() == 0) {
911 args.rval().setNaN();
912 return true;
915 return math_sin_handle(cx, args[0], args.rval());
918 bool
919 js::math_sqrt_handle(JSContext* cx, HandleValue number, MutableHandleValue result)
921 double x;
922 if (!ToNumber(cx, number, &x))
923 return false;
925 MathCache* mathCache = cx->runtime()->getMathCache(cx);
926 if (!mathCache)
927 return false;
929 double z = mathCache->lookup(sqrt, x, MathCache::Sqrt);
930 result.setDouble(z);
931 return true;
934 bool
935 js::math_sqrt(JSContext* cx, unsigned argc, Value* vp)
937 CallArgs args = CallArgsFromVp(argc, vp);
939 if (args.length() == 0) {
940 args.rval().setNaN();
941 return true;
944 return math_sqrt_handle(cx, args[0], args.rval());
947 double
948 js::math_tan_impl(MathCache* cache, double x)
950 return cache->lookup(tan, x, MathCache::Tan);
953 double
954 js::math_tan_uncached(double x)
956 return tan(x);
959 bool
960 js::math_tan(JSContext* cx, unsigned argc, Value* vp)
962 CallArgs args = CallArgsFromVp(argc, vp);
964 if (args.length() == 0) {
965 args.rval().setNaN();
966 return true;
969 double x;
970 if (!ToNumber(cx, args[0], &x))
971 return false;
973 MathCache* mathCache = cx->runtime()->getMathCache(cx);
974 if (!mathCache)
975 return false;
977 double z = math_tan_impl(mathCache, x);
978 args.rval().setDouble(z);
979 return true;
983 typedef double (*UnaryMathFunctionType)(MathCache* cache, double);
985 template <UnaryMathFunctionType F>
986 static bool math_function(JSContext* cx, unsigned argc, Value* vp)
988 CallArgs args = CallArgsFromVp(argc, vp);
989 if (args.length() == 0) {
990 args.rval().setNumber(GenericNaN());
991 return true;
994 double x;
995 if (!ToNumber(cx, args[0], &x))
996 return false;
998 MathCache* mathCache = cx->runtime()->getMathCache(cx);
999 if (!mathCache)
1000 return false;
1001 double z = F(mathCache, x);
1002 args.rval().setNumber(z);
1004 return true;
1009 double
1010 js::math_log10_impl(MathCache* cache, double x)
1012 return cache->lookup(log10, x, MathCache::Log10);
1015 double
1016 js::math_log10_uncached(double x)
1018 return log10(x);
1021 bool
1022 js::math_log10(JSContext* cx, unsigned argc, Value* vp)
1024 return math_function<math_log10_impl>(cx, argc, vp);
1027 #if !HAVE_LOG2
1028 double log2(double x)
1030 return log(x) / M_LN2;
1032 #endif
1034 double
1035 js::math_log2_impl(MathCache* cache, double x)
1037 return cache->lookup(log2, x, MathCache::Log2);
1040 double
1041 js::math_log2_uncached(double x)
1043 return log2(x);
1046 bool
1047 js::math_log2(JSContext* cx, unsigned argc, Value* vp)
1049 return math_function<math_log2_impl>(cx, argc, vp);
1052 #if !HAVE_LOG1P
1053 double log1p(double x)
1055 if (fabs(x) < 1e-4) {
1057 * Use Taylor approx. log(1 + x) = x - x^2 / 2 + x^3 / 3 - x^4 / 4 with error x^5 / 5
1058 * Since |x| < 10^-4, |x|^5 < 10^-20, relative error less than 10^-16
1060 double z = -(x * x * x * x) / 4 + (x * x * x) / 3 - (x * x) / 2 + x;
1061 return z;
1062 } else {
1063 /* For other large enough values of x use direct computation */
1064 return log(1.0 + x);
1067 #endif
1069 #ifdef __APPLE__
1070 // Ensure that log1p(-0) is -0.
1071 #define LOG1P_IF_OUT_OF_RANGE(x) if (x == 0) return x;
1072 #else
1073 #define LOG1P_IF_OUT_OF_RANGE(x)
1074 #endif
1076 double
1077 js::math_log1p_impl(MathCache* cache, double x)
1079 LOG1P_IF_OUT_OF_RANGE(x);
1080 return cache->lookup(log1p, x, MathCache::Log1p);
1083 double
1084 js::math_log1p_uncached(double x)
1086 LOG1P_IF_OUT_OF_RANGE(x);
1087 return log1p(x);
1090 #undef LOG1P_IF_OUT_OF_RANGE
1092 bool
1093 js::math_log1p(JSContext* cx, unsigned argc, Value* vp)
1095 return math_function<math_log1p_impl>(cx, argc, vp);
1098 #if !HAVE_EXPM1
1099 double expm1(double x)
1101 /* Special handling for -0 */
1102 if (x == 0.0)
1103 return x;
1105 if (fabs(x) < 1e-5) {
1107 * Use Taylor approx. exp(x) - 1 = x + x^2 / 2 + x^3 / 6 with error x^4 / 24
1108 * Since |x| < 10^-5, |x|^4 < 10^-20, relative error less than 10^-15
1110 double z = (x * x * x) / 6 + (x * x) / 2 + x;
1111 return z;
1112 } else {
1113 /* For other large enough values of x use direct computation */
1114 return exp(x) - 1.0;
1117 #endif
1119 double
1120 js::math_expm1_impl(MathCache* cache, double x)
1122 return cache->lookup(expm1, x, MathCache::Expm1);
1125 double
1126 js::math_expm1_uncached(double x)
1128 return expm1(x);
1131 bool
1132 js::math_expm1(JSContext* cx, unsigned argc, Value* vp)
1134 return math_function<math_expm1_impl>(cx, argc, vp);
1137 #if !HAVE_SQRT1PM1
1138 /* This algorithm computes sqrt(1+x)-1 for small x */
1139 double sqrt1pm1(double x)
1141 if (fabs(x) > 0.75)
1142 return sqrt(1 + x) - 1;
1144 return expm1(log1p(x) / 2);
1146 #endif
1149 double
1150 js::math_cosh_impl(MathCache* cache, double x)
1152 return cache->lookup(cosh, x, MathCache::Cosh);
1155 double
1156 js::math_cosh_uncached(double x)
1158 return cosh(x);
1161 bool
1162 js::math_cosh(JSContext* cx, unsigned argc, Value* vp)
1164 return math_function<math_cosh_impl>(cx, argc, vp);
1167 double
1168 js::math_sinh_impl(MathCache* cache, double x)
1170 return cache->lookup(sinh, x, MathCache::Sinh);
1173 double
1174 js::math_sinh_uncached(double x)
1176 return sinh(x);
1179 bool
1180 js::math_sinh(JSContext* cx, unsigned argc, Value* vp)
1182 return math_function<math_sinh_impl>(cx, argc, vp);
1185 double
1186 js::math_tanh_impl(MathCache* cache, double x)
1188 return cache->lookup(tanh, x, MathCache::Tanh);
1191 double
1192 js::math_tanh_uncached(double x)
1194 return tanh(x);
1197 bool
1198 js::math_tanh(JSContext* cx, unsigned argc, Value* vp)
1200 return math_function<math_tanh_impl>(cx, argc, vp);
1203 #if !HAVE_ACOSH
1204 double acosh(double x)
1206 const double SQUARE_ROOT_EPSILON = sqrt(std::numeric_limits<double>::epsilon());
1208 if ((x - 1) >= SQUARE_ROOT_EPSILON) {
1209 if (x > 1 / SQUARE_ROOT_EPSILON) {
1211 * http://functions.wolfram.com/ElementaryFunctions/ArcCosh/06/01/06/01/0001/
1212 * approximation by laurent series in 1/x at 0+ order from -1 to 0
1214 return log(x) + M_LN2;
1215 } else if (x < 1.5) {
1216 // This is just a rearrangement of the standard form below
1217 // devised to minimize loss of precision when x ~ 1:
1218 double y = x - 1;
1219 return log1p(y + sqrt(y * y + 2 * y));
1220 } else {
1221 // http://functions.wolfram.com/ElementaryFunctions/ArcCosh/02/
1222 return log(x + sqrt(x * x - 1));
1224 } else {
1225 // see http://functions.wolfram.com/ElementaryFunctions/ArcCosh/06/01/04/01/0001/
1226 double y = x - 1;
1227 // approximation by taylor series in y at 0 up to order 2.
1228 // If x is less than 1, sqrt(2 * y) is NaN and the result is NaN.
1229 return sqrt(2 * y) * (1 - y / 12 + 3 * y * y / 160);
1232 #endif
1234 double
1235 js::math_acosh_impl(MathCache* cache, double x)
1237 return cache->lookup(acosh, x, MathCache::Acosh);
1240 double
1241 js::math_acosh_uncached(double x)
1243 return acosh(x);
1246 bool
1247 js::math_acosh(JSContext* cx, unsigned argc, Value* vp)
1249 return math_function<math_acosh_impl>(cx, argc, vp);
1252 #if !HAVE_ASINH
1253 // Bug 899712 - gcc incorrectly rewrites -asinh(-x) to asinh(x) when overriding
1254 // asinh.
1255 static double my_asinh(double x)
1257 const double SQUARE_ROOT_EPSILON = sqrt(std::numeric_limits<double>::epsilon());
1258 const double FOURTH_ROOT_EPSILON = sqrt(SQUARE_ROOT_EPSILON);
1260 if (x >= FOURTH_ROOT_EPSILON) {
1261 if (x > 1 / SQUARE_ROOT_EPSILON)
1262 // http://functions.wolfram.com/ElementaryFunctions/ArcSinh/06/01/06/01/0001/
1263 // approximation by laurent series in 1/x at 0+ order from -1 to 1
1264 return M_LN2 + log(x) + 1 / (4 * x * x);
1265 else if (x < 0.5)
1266 return log1p(x + sqrt1pm1(x * x));
1267 else
1268 return log(x + sqrt(x * x + 1));
1269 } else if (x <= -FOURTH_ROOT_EPSILON) {
1270 return -my_asinh(-x);
1271 } else {
1272 // http://functions.wolfram.com/ElementaryFunctions/ArcSinh/06/01/03/01/0001/
1273 // approximation by taylor series in x at 0 up to order 2
1274 double result = x;
1276 if (fabs(x) >= SQUARE_ROOT_EPSILON) {
1277 double x3 = x * x * x;
1278 // approximation by taylor series in x at 0 up to order 4
1279 result -= x3 / 6;
1282 return result;
1285 #endif
1287 double
1288 js::math_asinh_impl(MathCache* cache, double x)
1290 #ifdef HAVE_ASINH
1291 return cache->lookup(asinh, x, MathCache::Asinh);
1292 #else
1293 return cache->lookup(my_asinh, x, MathCache::Asinh);
1294 #endif
1297 double
1298 js::math_asinh_uncached(double x)
1300 #ifdef HAVE_ASINH
1301 return asinh(x);
1302 #else
1303 return my_asinh(x);
1304 #endif
1307 bool
1308 js::math_asinh(JSContext* cx, unsigned argc, Value* vp)
1310 return math_function<math_asinh_impl>(cx, argc, vp);
1313 #if !HAVE_ATANH
1314 double atanh(double x)
1316 const double EPSILON = std::numeric_limits<double>::epsilon();
1317 const double SQUARE_ROOT_EPSILON = sqrt(EPSILON);
1318 const double FOURTH_ROOT_EPSILON = sqrt(SQUARE_ROOT_EPSILON);
1320 if (fabs(x) >= FOURTH_ROOT_EPSILON) {
1321 // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/02/
1322 if (fabs(x) < 0.5)
1323 return (log1p(x) - log1p(-x)) / 2;
1325 return log((1 + x) / (1 - x)) / 2;
1326 } else {
1327 // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/06/01/03/01/
1328 // approximation by taylor series in x at 0 up to order 2
1329 double result = x;
1331 if (fabs(x) >= SQUARE_ROOT_EPSILON) {
1332 double x3 = x * x * x;
1333 result += x3 / 3;
1336 return result;
1339 #endif
1341 double
1342 js::math_atanh_impl(MathCache* cache, double x)
1344 return cache->lookup(atanh, x, MathCache::Atanh);
1347 double
1348 js::math_atanh_uncached(double x)
1350 return atanh(x);
1353 bool
1354 js::math_atanh(JSContext* cx, unsigned argc, Value* vp)
1356 return math_function<math_atanh_impl>(cx, argc, vp);
1359 /* Consistency wrapper for platform deviations in hypot() */
1360 double
1361 js::ecmaHypot(double x, double y)
1363 #ifdef XP_WIN
1365 * Workaround MS hypot bug, where hypot(Infinity, NaN or Math.MIN_VALUE)
1366 * is NaN, not Infinity.
1368 if (mozilla::IsInfinite(x) || mozilla::IsInfinite(y)) {
1369 return mozilla::PositiveInfinity<double>();
1371 #endif
1372 return hypot(x, y);
1375 bool
1376 js::math_hypot(JSContext* cx, unsigned argc, Value* vp)
1378 CallArgs args = CallArgsFromVp(argc, vp);
1379 return math_hypot_handle(cx, args, args.rval());
1382 bool
1383 js::math_hypot_handle(JSContext* cx, HandleValueArray args, MutableHandleValue res)
1385 // IonMonkey calls the system hypot function directly if two arguments are
1386 // given. Do that here as well to get the same results.
1387 if (args.length() == 2) {
1388 double x, y;
1389 if (!ToNumber(cx, args[0], &x))
1390 return false;
1391 if (!ToNumber(cx, args[1], &y))
1392 return false;
1394 double result = ecmaHypot(x, y);
1395 res.setNumber(result);
1396 return true;
1399 bool isInfinite = false;
1400 bool isNaN = false;
1402 double scale = 0;
1403 double sumsq = 1;
1405 for (unsigned i = 0; i < args.length(); i++) {
1406 double x;
1407 if (!ToNumber(cx, args[i], &x))
1408 return false;
1410 isInfinite |= mozilla::IsInfinite(x);
1411 isNaN |= mozilla::IsNaN(x);
1413 double xabs = mozilla::Abs(x);
1415 if (scale < xabs) {
1416 sumsq = 1 + sumsq * (scale / xabs) * (scale / xabs);
1417 scale = xabs;
1418 } else if (scale != 0) {
1419 sumsq += (xabs / scale) * (xabs / scale);
1423 double result = isInfinite ? PositiveInfinity<double>() :
1424 isNaN ? GenericNaN() :
1425 scale * sqrt(sumsq);
1426 res.setNumber(result);
1427 return true;
1430 #if !HAVE_TRUNC
1431 double trunc(double x)
1433 return x > 0 ? floor(x) : ceil(x);
1435 #endif
1437 double
1438 js::math_trunc_impl(MathCache* cache, double x)
1440 return cache->lookup(trunc, x, MathCache::Trunc);
1443 double
1444 js::math_trunc_uncached(double x)
1446 return trunc(x);
1449 bool
1450 js::math_trunc(JSContext* cx, unsigned argc, Value* vp)
1452 return math_function<math_trunc_impl>(cx, argc, vp);
1455 static double sign(double x)
1457 if (mozilla::IsNaN(x))
1458 return GenericNaN();
1460 return x == 0 ? x : x < 0 ? -1 : 1;
1463 double
1464 js::math_sign_impl(MathCache* cache, double x)
1466 return cache->lookup(sign, x, MathCache::Sign);
1469 double
1470 js::math_sign_uncached(double x)
1472 return sign(x);
1475 bool
1476 js::math_sign(JSContext* cx, unsigned argc, Value* vp)
1478 return math_function<math_sign_impl>(cx, argc, vp);
1481 #if !HAVE_CBRT
1482 double cbrt(double x)
1484 if (x > 0) {
1485 return pow(x, 1.0 / 3.0);
1486 } else if (x == 0) {
1487 return x;
1488 } else {
1489 return -pow(-x, 1.0 / 3.0);
1492 #endif
1494 double
1495 js::math_cbrt_impl(MathCache* cache, double x)
1497 return cache->lookup(cbrt, x, MathCache::Cbrt);
1500 double
1501 js::math_cbrt_uncached(double x)
1503 return cbrt(x);
1506 bool
1507 js::math_cbrt(JSContext* cx, unsigned argc, Value* vp)
1509 return math_function<math_cbrt_impl>(cx, argc, vp);
1512 #if JS_HAS_TOSOURCE
1513 static bool
1514 math_toSource(JSContext* cx, unsigned argc, Value* vp)
1516 CallArgs args = CallArgsFromVp(argc, vp);
1517 args.rval().setString(cx->names().Math);
1518 return true;
1520 #endif
1522 static const JSFunctionSpec math_static_methods[] = {
1523 #if JS_HAS_TOSOURCE
1524 JS_FN(js_toSource_str, math_toSource, 0, 0),
1525 #endif
1526 JS_FN("abs", math_abs, 1, 0),
1527 JS_FN("acos", math_acos, 1, 0),
1528 JS_FN("asin", math_asin, 1, 0),
1529 JS_FN("atan", math_atan, 1, 0),
1530 JS_FN("atan2", math_atan2, 2, 0),
1531 JS_FN("ceil", math_ceil, 1, 0),
1532 JS_FN("clz32", math_clz32, 1, 0),
1533 JS_FN("cos", math_cos, 1, 0),
1534 JS_FN("exp", math_exp, 1, 0),
1535 JS_FN("floor", math_floor, 1, 0),
1536 JS_FN("imul", math_imul, 2, 0),
1537 JS_FN("fround", math_fround, 1, 0),
1538 JS_FN("log", math_log, 1, 0),
1539 JS_FN("max", math_max, 2, 0),
1540 JS_FN("min", math_min, 2, 0),
1541 JS_FN("pow", math_pow, 2, 0),
1542 JS_FN("random", math_random, 0, 0),
1543 JS_FN("round", math_round, 1, 0),
1544 JS_FN("sin", math_sin, 1, 0),
1545 JS_FN("sqrt", math_sqrt, 1, 0),
1546 JS_FN("tan", math_tan, 1, 0),
1547 JS_FN("log10", math_log10, 1, 0),
1548 JS_FN("log2", math_log2, 1, 0),
1549 JS_FN("log1p", math_log1p, 1, 0),
1550 JS_FN("expm1", math_expm1, 1, 0),
1551 JS_FN("cosh", math_cosh, 1, 0),
1552 JS_FN("sinh", math_sinh, 1, 0),
1553 JS_FN("tanh", math_tanh, 1, 0),
1554 JS_FN("acosh", math_acosh, 1, 0),
1555 JS_FN("asinh", math_asinh, 1, 0),
1556 JS_FN("atanh", math_atanh, 1, 0),
1557 JS_FN("hypot", math_hypot, 2, 0),
1558 JS_FN("trunc", math_trunc, 1, 0),
1559 JS_FN("sign", math_sign, 1, 0),
1560 JS_FN("cbrt", math_cbrt, 1, 0),
1561 JS_FS_END
1564 JSObject*
1565 js_InitMathClass(JSContext* cx, HandleObject obj)
1567 RootedObject proto(cx, obj->as<GlobalObject>().getOrCreateObjectPrototype(cx));
1568 if (!proto)
1569 return nullptr;
1570 RootedObject Math(cx, NewObjectWithGivenProto(cx, &MathClass, proto, obj, SingletonObject));
1571 if (!Math)
1572 return nullptr;
1574 if (!JS_DefineProperty(cx, obj, js_Math_str, Math, 0,
1575 JS_STUBGETTER, JS_STUBSETTER))
1577 return nullptr;
1580 if (!JS_DefineFunctions(cx, Math, math_static_methods))
1581 return nullptr;
1582 if (!JS_DefineConstDoubles(cx, Math, math_constants))
1583 return nullptr;
1585 obj->as<GlobalObject>().setConstructor(JSProto_Math, ObjectValue(*Math));
1587 return Math;