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/. */
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
28 #include "jscompartment.h"
29 #include "jslibmath.h"
33 #include "jsobjinlines.h"
38 using mozilla::NumberEqualsInt32
;
39 using mozilla::NumberIsInt32
;
40 using mozilla::ExponentComponent
;
41 using mozilla::FloatingPoint
;
42 using mozilla::IsFinite
;
43 using mozilla::IsInfinite
;
45 using mozilla::IsNegative
;
46 using mozilla::IsNegativeZero
;
47 using mozilla::PositiveInfinity
;
48 using mozilla::NegativeInfinity
;
52 static const JSConstDoubleSpec math_constants
[] = {
55 {"LOG10E" , M_LOG10E
},
60 {"SQRT1_2", M_SQRT1_2
},
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
));
74 MathCache::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf
)
76 return mallocSizeOf(this);
79 const Class
js::MathClass
= {
81 JSCLASS_HAS_CACHED_PROTO(JSProto_Math
)
85 js::math_abs_handle(JSContext
* cx
, js::HandleValue v
, js::MutableHandleValue r
)
88 if (!ToNumber(cx
, v
, &x
))
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();
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();
113 #define ACOS_IF_OUT_OF_RANGE(x)
117 js::math_acos_impl(MathCache
* cache
, double x
)
119 ACOS_IF_OUT_OF_RANGE(x
);
120 return cache
->lookup(acos
, x
, MathCache::Acos
);
124 js::math_acos_uncached(double x
)
126 ACOS_IF_OUT_OF_RANGE(x
);
130 #undef ACOS_IF_OUT_OF_RANGE
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();
143 if (!ToNumber(cx
, args
[0], &x
))
146 MathCache
* mathCache
= cx
->runtime()->getMathCache(cx
);
150 double z
= math_acos_impl(mathCache
, x
);
151 args
.rval().setDouble(z
);
155 #if defined(SOLARIS) && defined(__GNUC__)
156 #define ASIN_IF_OUT_OF_RANGE(x) if (x < -1 || 1 < x) return GenericNaN();
158 #define ASIN_IF_OUT_OF_RANGE(x)
162 js::math_asin_impl(MathCache
* cache
, double x
)
164 ASIN_IF_OUT_OF_RANGE(x
);
165 return cache
->lookup(asin
, x
, MathCache::Asin
);
169 js::math_asin_uncached(double x
)
171 ASIN_IF_OUT_OF_RANGE(x
);
175 #undef ASIN_IF_OUT_OF_RANGE
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();
188 if (!ToNumber(cx
, args
[0], &x
))
191 MathCache
* mathCache
= cx
->runtime()->getMathCache(cx
);
195 double z
= math_asin_impl(mathCache
, x
);
196 args
.rval().setDouble(z
);
201 js::math_atan_impl(MathCache
* cache
, double x
)
203 return cache
->lookup(atan
, x
, MathCache::Atan
);
207 js::math_atan_uncached(double x
)
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();
223 if (!ToNumber(cx
, args
[0], &x
))
226 MathCache
* mathCache
= cx
->runtime()->getMathCache(cx
);
230 double z
= math_atan_impl(mathCache
, x
);
231 args
.rval().setDouble(z
);
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
);
254 #if defined(SOLARIS) && defined(__GNUC__)
256 if (IsNegativeZero(x
))
257 return js_copysign(M_PI
, y
);
266 js::math_atan2_handle(JSContext
* cx
, HandleValue y
, HandleValue x
, MutableHandleValue res
)
269 if (!ToNumber(cx
, y
, &dy
))
273 if (!ToNumber(cx
, x
, &dx
))
276 double z
= ecmaAtan2(dy
, dx
);
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());
290 js::math_ceil_impl(double x
)
293 if (x
< 0 && x
> -1.0)
294 return js_copysign(0, -1);
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();
310 if (!ToNumber(cx
, args
[0], &x
))
313 double z
= math_ceil_impl(x
);
314 args
.rval().setNumber(z
);
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);
329 if (!ToUint32(cx
, args
[0], &n
))
333 args
.rval().setInt32(32);
337 args
.rval().setInt32(mozilla::CountLeadingZeroes32(n
));
342 js::math_cos_impl(MathCache
* cache
, double x
)
344 return cache
->lookup(cos
, x
, MathCache::Cos
);
348 js::math_cos_uncached(double x
)
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();
364 if (!ToNumber(cx
, args
[0], &x
))
367 MathCache
* mathCache
= cx
->runtime()->getMathCache(cx
);
371 double z
= math_cos_impl(mathCache
, x
);
372 args
.rval().setDouble(z
);
377 #define EXP_IF_OUT_OF_RANGE(x) \
379 if (x == PositiveInfinity<double>()) \
380 return PositiveInfinity<double>(); \
381 if (x == NegativeInfinity<double>()) \
385 #define EXP_IF_OUT_OF_RANGE(x)
389 js::math_exp_impl(MathCache
* cache
, double x
)
391 EXP_IF_OUT_OF_RANGE(x
);
392 return cache
->lookup(exp
, x
, MathCache::Exp
);
396 js::math_exp_uncached(double x
)
398 EXP_IF_OUT_OF_RANGE(x
);
402 #undef EXP_IF_OUT_OF_RANGE
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();
415 if (!ToNumber(cx
, args
[0], &x
))
418 MathCache
* mathCache
= cx
->runtime()->getMathCache(cx
);
422 double z
= math_exp_impl(mathCache
, x
);
423 args
.rval().setNumber(z
);
428 js::math_floor_impl(double x
)
434 js::math_floor_handle(JSContext
* cx
, HandleValue v
, MutableHandleValue r
)
437 if(!ToNumber(cx
, v
, &d
))
440 double z
= math_floor_impl(d
);
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();
456 return math_floor_handle(cx
, args
[0], args
.rval());
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
))
467 if (args
.hasDefined(1) && !ToUint32(cx
, args
[1], &b
))
470 uint32_t product
= a
* b
;
471 args
.rval().setInt32(product
> INT32_MAX
472 ? int32_t(INT32_MIN
+ (product
- INT32_MAX
- 1))
477 // Implements Math.fround (20.2.2.16) up to step 3
479 js::RoundFloat32(JSContext
* cx
, HandleValue v
, float* out
)
482 bool success
= ToNumber(cx
, v
, &d
);
483 *out
= static_cast<float>(d
);
488 js::RoundFloat32(JSContext
* cx
, HandleValue arg
, MutableHandleValue res
)
491 if (!RoundFloat32(cx
, arg
, &f
))
494 res
.setDouble(static_cast<double>(f
));
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();
509 if (!RoundFloat32(cx
, args
[0], &f
))
512 args
.rval().setDouble(static_cast<double>(f
));
516 #if defined(SOLARIS) && defined(__GNUC__)
517 #define LOG_IF_OUT_OF_RANGE(x) if (x < 0) return GenericNaN();
519 #define LOG_IF_OUT_OF_RANGE(x)
523 js::math_log_impl(MathCache
* cache
, double x
)
525 LOG_IF_OUT_OF_RANGE(x
);
526 return cache
->lookup(log
, x
, MathCache::Log
);
530 js::math_log_uncached(double x
)
532 LOG_IF_OUT_OF_RANGE(x
);
536 #undef LOG_IF_OUT_OF_RANGE
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();
549 if (!ToNumber(cx
, args
[0], &x
))
552 MathCache
* mathCache
= cx
->runtime()->getMathCache(cx
);
556 double z
= math_log_impl(mathCache
, x
);
557 args
.rval().setNumber(z
);
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
)))
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
++) {
578 if (!ToNumber(cx
, args
[i
], &x
))
580 maxval
= math_max_impl(x
, maxval
);
582 args
.rval().setNumber(maxval
);
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
)))
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
++) {
603 if (!ToNumber(cx
, args
[i
], &x
))
605 minval
= math_min_impl(x
, minval
);
607 args
.rval().setNumber(minval
);
612 js::minmax_impl(JSContext
* cx
, bool max
, HandleValue a
, HandleValue b
, MutableHandleValue res
)
616 if (!ToNumber(cx
, a
, &x
))
618 if (!ToNumber(cx
, b
, &y
))
622 res
.setNumber(math_max_impl(x
, y
));
624 res
.setNumber(math_min_impl(x
, y
));
630 js::powi(double x
, int y
)
632 unsigned n
= (y
< 0) ? -y
: y
;
636 if ((n
& 1) != 0) p
*= m
;
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).
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.
665 if (NumberEqualsInt32(y
, &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))
675 /* pow(x, +-0) is always 1, even for x = NaN (MSVC gets this wrong). */
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) {
687 return 1.0 / sqrt(x
);
693 js::math_pow_handle(JSContext
* cx
, HandleValue base
, HandleValue power
, MutableHandleValue result
)
696 if (!ToNumber(cx
, base
, &x
))
700 if (!ToNumber(cx
, power
, &y
))
703 double z
= ecmaPow(x
, y
);
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());
717 random_generateSeed()
728 * Our PRNG only uses 48 bits, so calling rand_s() twice to get 64 bits is
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");
740 (void)read(fd
, seed
.u8
, mozilla::ArrayLength(seed
.u8
));
745 # error "Platform needs to implement random_generateSeed()"
748 seed
.u32
[1] ^= PRMJ_Now();
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.
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
;
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
);
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
);
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
);
802 js::math_round_handle(JSContext
* cx
, HandleValue arg
, MutableHandleValue res
)
805 if (!ToNumber(cx
, arg
, &d
))
808 d
= math_round_impl(d
);
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
);
829 js::math_round_impl(double x
)
832 if (NumberIsInt32(x
, &ignored
))
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
))
839 double add
= (x
>= 0) ? GetBiggestNumberLessThan(0.5) : 0.5;
840 return js_copysign(floor(x
+ add
), x
);
844 js::math_roundf_impl(float x
)
847 if (NumberIsInt32(x
, &ignored
))
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
))
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();
868 return math_round_handle(cx
, args
[0], args
.rval());
872 js::math_sin_impl(MathCache
* cache
, double x
)
874 return cache
->lookup(math_sin_uncached
, x
, MathCache::Sin
);
878 js::math_sin_uncached(double x
)
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
))
890 js::math_sin_handle(JSContext
* cx
, HandleValue val
, MutableHandleValue res
)
893 if (!ToNumber(cx
, val
, &in
))
896 MathCache
* mathCache
= cx
->runtime()->getMathCache(cx
);
900 double out
= math_sin_impl(mathCache
, in
);
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();
915 return math_sin_handle(cx
, args
[0], args
.rval());
919 js::math_sqrt_handle(JSContext
* cx
, HandleValue number
, MutableHandleValue result
)
922 if (!ToNumber(cx
, number
, &x
))
925 MathCache
* mathCache
= cx
->runtime()->getMathCache(cx
);
929 double z
= mathCache
->lookup(sqrt
, x
, MathCache::Sqrt
);
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();
944 return math_sqrt_handle(cx
, args
[0], args
.rval());
948 js::math_tan_impl(MathCache
* cache
, double x
)
950 return cache
->lookup(tan
, x
, MathCache::Tan
);
954 js::math_tan_uncached(double x
)
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();
970 if (!ToNumber(cx
, args
[0], &x
))
973 MathCache
* mathCache
= cx
->runtime()->getMathCache(cx
);
977 double z
= math_tan_impl(mathCache
, x
);
978 args
.rval().setDouble(z
);
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());
995 if (!ToNumber(cx
, args
[0], &x
))
998 MathCache
* mathCache
= cx
->runtime()->getMathCache(cx
);
1001 double z
= F(mathCache
, x
);
1002 args
.rval().setNumber(z
);
1010 js::math_log10_impl(MathCache
* cache
, double x
)
1012 return cache
->lookup(log10
, x
, MathCache::Log10
);
1016 js::math_log10_uncached(double x
)
1022 js::math_log10(JSContext
* cx
, unsigned argc
, Value
* vp
)
1024 return math_function
<math_log10_impl
>(cx
, argc
, vp
);
1028 double log2(double x
)
1030 return log(x
) / M_LN2
;
1035 js::math_log2_impl(MathCache
* cache
, double x
)
1037 return cache
->lookup(log2
, x
, MathCache::Log2
);
1041 js::math_log2_uncached(double x
)
1047 js::math_log2(JSContext
* cx
, unsigned argc
, Value
* vp
)
1049 return math_function
<math_log2_impl
>(cx
, argc
, vp
);
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
;
1063 /* For other large enough values of x use direct computation */
1064 return log(1.0 + x
);
1070 // Ensure that log1p(-0) is -0.
1071 #define LOG1P_IF_OUT_OF_RANGE(x) if (x == 0) return x;
1073 #define LOG1P_IF_OUT_OF_RANGE(x)
1077 js::math_log1p_impl(MathCache
* cache
, double x
)
1079 LOG1P_IF_OUT_OF_RANGE(x
);
1080 return cache
->lookup(log1p
, x
, MathCache::Log1p
);
1084 js::math_log1p_uncached(double x
)
1086 LOG1P_IF_OUT_OF_RANGE(x
);
1090 #undef LOG1P_IF_OUT_OF_RANGE
1093 js::math_log1p(JSContext
* cx
, unsigned argc
, Value
* vp
)
1095 return math_function
<math_log1p_impl
>(cx
, argc
, vp
);
1099 double expm1(double x
)
1101 /* Special handling for -0 */
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
;
1113 /* For other large enough values of x use direct computation */
1114 return exp(x
) - 1.0;
1120 js::math_expm1_impl(MathCache
* cache
, double x
)
1122 return cache
->lookup(expm1
, x
, MathCache::Expm1
);
1126 js::math_expm1_uncached(double x
)
1132 js::math_expm1(JSContext
* cx
, unsigned argc
, Value
* vp
)
1134 return math_function
<math_expm1_impl
>(cx
, argc
, vp
);
1138 /* This algorithm computes sqrt(1+x)-1 for small x */
1139 double sqrt1pm1(double x
)
1142 return sqrt(1 + x
) - 1;
1144 return expm1(log1p(x
) / 2);
1150 js::math_cosh_impl(MathCache
* cache
, double x
)
1152 return cache
->lookup(cosh
, x
, MathCache::Cosh
);
1156 js::math_cosh_uncached(double x
)
1162 js::math_cosh(JSContext
* cx
, unsigned argc
, Value
* vp
)
1164 return math_function
<math_cosh_impl
>(cx
, argc
, vp
);
1168 js::math_sinh_impl(MathCache
* cache
, double x
)
1170 return cache
->lookup(sinh
, x
, MathCache::Sinh
);
1174 js::math_sinh_uncached(double x
)
1180 js::math_sinh(JSContext
* cx
, unsigned argc
, Value
* vp
)
1182 return math_function
<math_sinh_impl
>(cx
, argc
, vp
);
1186 js::math_tanh_impl(MathCache
* cache
, double x
)
1188 return cache
->lookup(tanh
, x
, MathCache::Tanh
);
1192 js::math_tanh_uncached(double x
)
1198 js::math_tanh(JSContext
* cx
, unsigned argc
, Value
* vp
)
1200 return math_function
<math_tanh_impl
>(cx
, argc
, vp
);
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:
1219 return log1p(y
+ sqrt(y
* y
+ 2 * y
));
1221 // http://functions.wolfram.com/ElementaryFunctions/ArcCosh/02/
1222 return log(x
+ sqrt(x
* x
- 1));
1225 // see http://functions.wolfram.com/ElementaryFunctions/ArcCosh/06/01/04/01/0001/
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);
1235 js::math_acosh_impl(MathCache
* cache
, double x
)
1237 return cache
->lookup(acosh
, x
, MathCache::Acosh
);
1241 js::math_acosh_uncached(double x
)
1247 js::math_acosh(JSContext
* cx
, unsigned argc
, Value
* vp
)
1249 return math_function
<math_acosh_impl
>(cx
, argc
, vp
);
1253 // Bug 899712 - gcc incorrectly rewrites -asinh(-x) to asinh(x) when overriding
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
);
1266 return log1p(x
+ sqrt1pm1(x
* x
));
1268 return log(x
+ sqrt(x
* x
+ 1));
1269 } else if (x
<= -FOURTH_ROOT_EPSILON
) {
1270 return -my_asinh(-x
);
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
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
1288 js::math_asinh_impl(MathCache
* cache
, double x
)
1291 return cache
->lookup(asinh
, x
, MathCache::Asinh
);
1293 return cache
->lookup(my_asinh
, x
, MathCache::Asinh
);
1298 js::math_asinh_uncached(double x
)
1308 js::math_asinh(JSContext
* cx
, unsigned argc
, Value
* vp
)
1310 return math_function
<math_asinh_impl
>(cx
, argc
, vp
);
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/
1323 return (log1p(x
) - log1p(-x
)) / 2;
1325 return log((1 + x
) / (1 - x
)) / 2;
1327 // http://functions.wolfram.com/ElementaryFunctions/ArcTanh/06/01/03/01/
1328 // approximation by taylor series in x at 0 up to order 2
1331 if (fabs(x
) >= SQUARE_ROOT_EPSILON
) {
1332 double x3
= x
* x
* x
;
1342 js::math_atanh_impl(MathCache
* cache
, double x
)
1344 return cache
->lookup(atanh
, x
, MathCache::Atanh
);
1348 js::math_atanh_uncached(double x
)
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() */
1361 js::ecmaHypot(double x
, double y
)
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>();
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());
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) {
1389 if (!ToNumber(cx
, args
[0], &x
))
1391 if (!ToNumber(cx
, args
[1], &y
))
1394 double result
= ecmaHypot(x
, y
);
1395 res
.setNumber(result
);
1399 bool isInfinite
= false;
1405 for (unsigned i
= 0; i
< args
.length(); i
++) {
1407 if (!ToNumber(cx
, args
[i
], &x
))
1410 isInfinite
|= mozilla::IsInfinite(x
);
1411 isNaN
|= mozilla::IsNaN(x
);
1413 double xabs
= mozilla::Abs(x
);
1416 sumsq
= 1 + sumsq
* (scale
/ xabs
) * (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
);
1431 double trunc(double x
)
1433 return x
> 0 ? floor(x
) : ceil(x
);
1438 js::math_trunc_impl(MathCache
* cache
, double x
)
1440 return cache
->lookup(trunc
, x
, MathCache::Trunc
);
1444 js::math_trunc_uncached(double x
)
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;
1464 js::math_sign_impl(MathCache
* cache
, double x
)
1466 return cache
->lookup(sign
, x
, MathCache::Sign
);
1470 js::math_sign_uncached(double x
)
1476 js::math_sign(JSContext
* cx
, unsigned argc
, Value
* vp
)
1478 return math_function
<math_sign_impl
>(cx
, argc
, vp
);
1482 double cbrt(double x
)
1485 return pow(x
, 1.0 / 3.0);
1486 } else if (x
== 0) {
1489 return -pow(-x
, 1.0 / 3.0);
1495 js::math_cbrt_impl(MathCache
* cache
, double x
)
1497 return cache
->lookup(cbrt
, x
, MathCache::Cbrt
);
1501 js::math_cbrt_uncached(double x
)
1507 js::math_cbrt(JSContext
* cx
, unsigned argc
, Value
* vp
)
1509 return math_function
<math_cbrt_impl
>(cx
, argc
, vp
);
1514 math_toSource(JSContext
* cx
, unsigned argc
, Value
* vp
)
1516 CallArgs args
= CallArgsFromVp(argc
, vp
);
1517 args
.rval().setString(cx
->names().Math
);
1522 static const JSFunctionSpec math_static_methods
[] = {
1524 JS_FN(js_toSource_str
, math_toSource
, 0, 0),
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),
1565 js_InitMathClass(JSContext
* cx
, HandleObject obj
)
1567 RootedObject
proto(cx
, obj
->as
<GlobalObject
>().getOrCreateObjectPrototype(cx
));
1570 RootedObject
Math(cx
, NewObjectWithGivenProto(cx
, &MathClass
, proto
, obj
, SingletonObject
));
1574 if (!JS_DefineProperty(cx
, obj
, js_Math_str
, Math
, 0,
1575 JS_STUBGETTER
, JS_STUBSETTER
))
1580 if (!JS_DefineFunctions(cx
, Math
, math_static_methods
))
1582 if (!JS_DefineConstDoubles(cx
, Math
, math_constants
))
1585 obj
->as
<GlobalObject
>().setConstructor(JSProto_Math
, ObjectValue(*Math
));