MathObject.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960
  1. /*
  2. * Copyright (c) 2020-2023, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
  4. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  5. * Copyright (c) 2023, Shannon Booth <shannon@serenityos.org>
  6. *
  7. * SPDX-License-Identifier: BSD-2-Clause
  8. */
  9. #include <AK/BuiltinWrappers.h>
  10. #include <AK/Function.h>
  11. #include <AK/Random.h>
  12. #include <LibJS/Runtime/GlobalObject.h>
  13. #include <LibJS/Runtime/MathObject.h>
  14. #include <LibJS/Runtime/ValueInlines.h>
  15. #include <math.h>
  16. namespace JS {
  17. GC_DEFINE_ALLOCATOR(MathObject);
  18. MathObject::MathObject(Realm& realm)
  19. : Object(ConstructWithPrototypeTag::Tag, realm.intrinsics().object_prototype())
  20. {
  21. }
  22. void MathObject::initialize(Realm& realm)
  23. {
  24. auto& vm = this->vm();
  25. Base::initialize(realm);
  26. u8 attr = Attribute::Writable | Attribute::Configurable;
  27. define_native_function(realm, vm.names.abs, abs, 1, attr, Bytecode::Builtin::MathAbs);
  28. define_native_function(realm, vm.names.random, random, 0, attr);
  29. define_native_function(realm, vm.names.sqrt, sqrt, 1, attr, Bytecode::Builtin::MathSqrt);
  30. define_native_function(realm, vm.names.floor, floor, 1, attr, Bytecode::Builtin::MathFloor);
  31. define_native_function(realm, vm.names.ceil, ceil, 1, attr, Bytecode::Builtin::MathCeil);
  32. define_native_function(realm, vm.names.round, round, 1, attr, Bytecode::Builtin::MathRound);
  33. define_native_function(realm, vm.names.max, max, 2, attr);
  34. define_native_function(realm, vm.names.min, min, 2, attr);
  35. define_native_function(realm, vm.names.trunc, trunc, 1, attr);
  36. define_native_function(realm, vm.names.sin, sin, 1, attr);
  37. define_native_function(realm, vm.names.cos, cos, 1, attr);
  38. define_native_function(realm, vm.names.tan, tan, 1, attr);
  39. define_native_function(realm, vm.names.pow, pow, 2, attr, Bytecode::Builtin::MathPow);
  40. define_native_function(realm, vm.names.exp, exp, 1, attr, Bytecode::Builtin::MathExp);
  41. define_native_function(realm, vm.names.expm1, expm1, 1, attr);
  42. define_native_function(realm, vm.names.sign, sign, 1, attr);
  43. define_native_function(realm, vm.names.clz32, clz32, 1, attr);
  44. define_native_function(realm, vm.names.acos, acos, 1, attr);
  45. define_native_function(realm, vm.names.acosh, acosh, 1, attr);
  46. define_native_function(realm, vm.names.asin, asin, 1, attr);
  47. define_native_function(realm, vm.names.asinh, asinh, 1, attr);
  48. define_native_function(realm, vm.names.atan, atan, 1, attr);
  49. define_native_function(realm, vm.names.atanh, atanh, 1, attr);
  50. define_native_function(realm, vm.names.log1p, log1p, 1, attr);
  51. define_native_function(realm, vm.names.cbrt, cbrt, 1, attr);
  52. define_native_function(realm, vm.names.atan2, atan2, 2, attr);
  53. define_native_function(realm, vm.names.fround, fround, 1, attr);
  54. define_native_function(realm, vm.names.f16round, f16round, 1, attr);
  55. define_native_function(realm, vm.names.hypot, hypot, 2, attr);
  56. define_native_function(realm, vm.names.imul, imul, 2, attr);
  57. define_native_function(realm, vm.names.log, log, 1, attr, Bytecode::Builtin::MathLog);
  58. define_native_function(realm, vm.names.log2, log2, 1, attr);
  59. define_native_function(realm, vm.names.log10, log10, 1, attr);
  60. define_native_function(realm, vm.names.sinh, sinh, 1, attr);
  61. define_native_function(realm, vm.names.cosh, cosh, 1, attr);
  62. define_native_function(realm, vm.names.tanh, tanh, 1, attr);
  63. // 21.3.1 Value Properties of the Math Object, https://tc39.es/ecma262/#sec-value-properties-of-the-math-object
  64. define_direct_property(vm.names.E, Value(M_E), 0);
  65. define_direct_property(vm.names.LN2, Value(M_LN2), 0);
  66. define_direct_property(vm.names.LN10, Value(M_LN10), 0);
  67. define_direct_property(vm.names.LOG2E, Value(::log2(M_E)), 0);
  68. define_direct_property(vm.names.LOG10E, Value(::log10(M_E)), 0);
  69. define_direct_property(vm.names.PI, Value(M_PI), 0);
  70. define_direct_property(vm.names.SQRT1_2, Value(M_SQRT1_2), 0);
  71. define_direct_property(vm.names.SQRT2, Value(M_SQRT2), 0);
  72. // 21.3.1.9 Math [ @@toStringTag ], https://tc39.es/ecma262/#sec-math-@@tostringtag
  73. define_direct_property(vm.well_known_symbol_to_string_tag(), PrimitiveString::create(vm, vm.names.Math.as_string()), Attribute::Configurable);
  74. }
  75. // 21.3.2.1 Math.abs ( x ), https://tc39.es/ecma262/#sec-math.abs
  76. ThrowCompletionOr<Value> MathObject::abs_impl(VM& vm, Value x)
  77. {
  78. // OPTIMIZATION: Fast path for Int32 values.
  79. if (x.is_int32())
  80. return Value(AK::abs(x.as_i32()));
  81. // Let n be ? ToNumber(x).
  82. auto number = TRY(x.to_number(vm));
  83. // 2. If n is NaN, return NaN.
  84. if (number.is_nan())
  85. return js_nan();
  86. // 3. If n is -0๐”ฝ, return +0๐”ฝ.
  87. if (number.is_negative_zero())
  88. return Value(0);
  89. // 4. If n is -โˆž๐”ฝ, return +โˆž๐”ฝ.
  90. if (number.is_negative_infinity())
  91. return js_infinity();
  92. // 5. If n < -0๐”ฝ, return -n.
  93. // 6. Return n.
  94. return Value(number.as_double() < 0 ? -number.as_double() : number.as_double());
  95. }
  96. // 21.3.2.1 Math.abs ( x ), https://tc39.es/ecma262/#sec-math.abs
  97. JS_DEFINE_NATIVE_FUNCTION(MathObject::abs)
  98. {
  99. return abs_impl(vm, vm.argument(0));
  100. }
  101. // 21.3.2.2 Math.acos ( x ), https://tc39.es/ecma262/#sec-math.acos
  102. JS_DEFINE_NATIVE_FUNCTION(MathObject::acos)
  103. {
  104. // 1. Let n be ? ToNumber(x).
  105. auto number = TRY(vm.argument(0).to_number(vm));
  106. // 2. If n is NaN, n > 1๐”ฝ, or n < -1๐”ฝ, return NaN.
  107. if (number.is_nan() || number.as_double() > 1 || number.as_double() < -1)
  108. return js_nan();
  109. // 3. If n is 1๐”ฝ, return +0๐”ฝ.
  110. if (number.as_double() == 1)
  111. return Value(0);
  112. // 4. Return an implementation-approximated Number value representing the result of the inverse cosine of โ„(n).
  113. return Value(::acos(number.as_double()));
  114. }
  115. // 21.3.2.3 Math.acosh ( x ), https://tc39.es/ecma262/#sec-math.acosh
  116. JS_DEFINE_NATIVE_FUNCTION(MathObject::acosh)
  117. {
  118. // 1. Let n be ? ToNumber(x).
  119. auto number = TRY(vm.argument(0).to_number(vm));
  120. // 2. If n is NaN or n is +โˆž๐”ฝ, return n.
  121. if (number.is_nan() || number.is_positive_infinity())
  122. return number;
  123. // 3. If n is 1๐”ฝ, return +0๐”ฝ.
  124. if (number.as_double() == 1.0)
  125. return Value(0.0);
  126. // 4. If n < 1๐”ฝ, return NaN.
  127. if (number.as_double() < 1)
  128. return js_nan();
  129. // 5. Return an implementation-approximated Number value representing the result of the inverse hyperbolic cosine of โ„(n).
  130. return Value(::acosh(number.as_double()));
  131. }
  132. // 21.3.2.4 Math.asin ( x ), https://tc39.es/ecma262/#sec-math.asin
  133. JS_DEFINE_NATIVE_FUNCTION(MathObject::asin)
  134. {
  135. // 1. Let n be ? ToNumber(x).
  136. auto number = TRY(vm.argument(0).to_number(vm));
  137. // 2. If n is NaN, n is +0๐”ฝ, or n is -0๐”ฝ, return n.
  138. if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
  139. return number;
  140. // 3. If n > 1๐”ฝ or n < -1๐”ฝ, return NaN.
  141. if (number.as_double() > 1 || number.as_double() < -1)
  142. return js_nan();
  143. // 4. Return an implementation-approximated Number value representing the result of the inverse sine of โ„(n).
  144. return Value(::asin(number.as_double()));
  145. }
  146. // 21.3.2.5 Math.asinh ( x ), https://tc39.es/ecma262/#sec-math.asinh
  147. JS_DEFINE_NATIVE_FUNCTION(MathObject::asinh)
  148. {
  149. // 1. Let n be ? ToNumber(x).
  150. auto number = TRY(vm.argument(0).to_number(vm));
  151. // 2. If n is not finite or n is either +0๐”ฝ or -0๐”ฝ, return n.
  152. if (!number.is_finite_number() || number.is_positive_zero() || number.is_negative_zero())
  153. return number;
  154. // 3. Return an implementation-approximated Number value representing the result of the inverse hyperbolic sine of โ„(n).
  155. return Value(::asinh(number.as_double()));
  156. }
  157. // 21.3.2.6 Math.atan ( x ), https://tc39.es/ecma262/#sec-math.atan
  158. JS_DEFINE_NATIVE_FUNCTION(MathObject::atan)
  159. {
  160. // Let n be ? ToNumber(x).
  161. auto number = TRY(vm.argument(0).to_number(vm));
  162. // 2. If n is one of NaN, +0๐”ฝ, or -0๐”ฝ, return n.
  163. if (number.is_nan() || number.as_double() == 0)
  164. return number;
  165. // 3. If n is +โˆž๐”ฝ, return an implementation-approximated Number value representing ฯ€ / 2.
  166. if (number.is_positive_infinity())
  167. return Value(M_PI_2);
  168. // 4. If n is -โˆž๐”ฝ, return an implementation-approximated Number value representing -ฯ€ / 2.
  169. if (number.is_negative_infinity())
  170. return Value(-M_PI_2);
  171. // 5. Return an implementation-approximated Number value representing the result of the inverse tangent of โ„(n).
  172. return Value(::atan(number.as_double()));
  173. }
  174. // 21.3.2.7 Math.atanh ( x ), https://tc39.es/ecma262/#sec-math.atanh
  175. JS_DEFINE_NATIVE_FUNCTION(MathObject::atanh)
  176. {
  177. // 1. Let n be ? ToNumber(x).
  178. auto number = TRY(vm.argument(0).to_number(vm));
  179. // 2. If n is NaN, n is +0๐”ฝ, or n is -0๐”ฝ, return n.
  180. if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
  181. return number;
  182. // 3. If n > 1๐”ฝ or n < -1๐”ฝ, return NaN.
  183. if (number.as_double() > 1. || number.as_double() < -1.)
  184. return js_nan();
  185. // 4. If n is 1๐”ฝ, return +โˆž๐”ฝ.
  186. if (number.as_double() == 1.)
  187. return js_infinity();
  188. // 5. If n is -1๐”ฝ, return -โˆž๐”ฝ.
  189. if (number.as_double() == -1.)
  190. return js_negative_infinity();
  191. // 6. Return an implementation-approximated Number value representing the result of the inverse hyperbolic tangent of โ„(n).
  192. return Value(::atanh(number.as_double()));
  193. }
  194. // 21.3.2.8 Math.atan2 ( y, x ), https://tc39.es/ecma262/#sec-math.atan2
  195. JS_DEFINE_NATIVE_FUNCTION(MathObject::atan2)
  196. {
  197. auto constexpr three_quarters_pi = M_PI_4 + M_PI_2;
  198. // 1. Let ny be ? ToNumber(y).
  199. auto y = TRY(vm.argument(0).to_number(vm));
  200. // 2. Let nx be ? ToNumber(x).
  201. auto x = TRY(vm.argument(1).to_number(vm));
  202. // 3. If ny is NaN or nx is NaN, return NaN.
  203. if (y.is_nan() || x.is_nan())
  204. return js_nan();
  205. // 4. If ny is +โˆž๐”ฝ, then
  206. if (y.is_positive_infinity()) {
  207. // a. If nx is +โˆž๐”ฝ, return an implementation-approximated Number value representing ฯ€ / 4.
  208. if (x.is_positive_infinity())
  209. return Value(M_PI_4);
  210. // b. If nx is -โˆž๐”ฝ, return an implementation-approximated Number value representing 3ฯ€ / 4.
  211. if (x.is_negative_infinity())
  212. return Value(three_quarters_pi);
  213. // c. Return an implementation-approximated Number value representing ฯ€ / 2.
  214. return Value(M_PI_2);
  215. }
  216. // 5. If ny is -โˆž๐”ฝ, then
  217. if (y.is_negative_infinity()) {
  218. // a. If nx is +โˆž๐”ฝ, return an implementation-approximated Number value representing -ฯ€ / 4.
  219. if (x.is_positive_infinity())
  220. return Value(-M_PI_4);
  221. // b. If nx is -โˆž๐”ฝ, return an implementation-approximated Number value representing -3ฯ€ / 4.
  222. if (x.is_negative_infinity())
  223. return Value(-three_quarters_pi);
  224. // c. Return an implementation-approximated Number value representing -ฯ€ / 2.
  225. return Value(-M_PI_2);
  226. }
  227. // 6. If ny is +0๐”ฝ, then
  228. if (y.is_positive_zero()) {
  229. // a. If nx > +0๐”ฝ or nx is +0๐”ฝ, return +0๐”ฝ.
  230. if (x.as_double() > 0 || x.is_positive_zero())
  231. return Value(0.0);
  232. // b. Return an implementation-approximated Number value representing ฯ€.
  233. return Value(M_PI);
  234. }
  235. // 7. If ny is -0๐”ฝ, then
  236. if (y.is_negative_zero()) {
  237. // a. If nx > +0๐”ฝ or nx is +0๐”ฝ, return -0๐”ฝ
  238. if (x.as_double() > 0 || x.is_positive_zero())
  239. return Value(-0.0);
  240. // b. Return an implementation-approximated Number value representing -ฯ€.
  241. return Value(-M_PI);
  242. }
  243. // 8. Assert: ny is finite and is neither +0๐”ฝ nor -0๐”ฝ.
  244. VERIFY(y.is_finite_number() && !y.is_positive_zero() && !y.is_negative_zero());
  245. // 9. If ny > +0๐”ฝ, then
  246. if (y.as_double() > 0) {
  247. // a. If nx is +โˆž๐”ฝ, return +0๐”ฝ.
  248. if (x.is_positive_infinity())
  249. return Value(0);
  250. // b. If nx is -โˆž๐”ฝ, return an implementation-approximated Number value representing ฯ€.
  251. if (x.is_negative_infinity())
  252. return Value(M_PI);
  253. // c. If nx is either +0๐”ฝ or -0๐”ฝ, return an implementation-approximated Number value representing ฯ€ / 2.
  254. if (x.is_positive_zero() || x.is_negative_zero())
  255. return Value(M_PI_2);
  256. }
  257. // 10. If ny < -0๐”ฝ, then
  258. if (y.as_double() < -0) {
  259. // a. If nx is +โˆž๐”ฝ, return -0๐”ฝ.
  260. if (x.is_positive_infinity())
  261. return Value(-0.0);
  262. // b. If nx is -โˆž๐”ฝ, return an implementation-approximated Number value representing -ฯ€.
  263. if (x.is_negative_infinity())
  264. return Value(-M_PI);
  265. // c. If nx is either +0๐”ฝ or -0๐”ฝ, return an implementation-approximated Number value representing -ฯ€ / 2.
  266. if (x.is_positive_zero() || x.is_negative_zero())
  267. return Value(-M_PI_2);
  268. }
  269. // 11. Assert: nx is finite and is neither +0๐”ฝ nor -0๐”ฝ.
  270. VERIFY(x.is_finite_number() && !x.is_positive_zero() && !x.is_negative_zero());
  271. // 12. Return an implementation-approximated Number value representing the result of the inverse tangent of the quotient โ„(ny) / โ„(nx).
  272. return Value(::atan2(y.as_double(), x.as_double()));
  273. }
  274. // 21.3.2.9 Math.cbrt ( x ), https://tc39.es/ecma262/#sec-math.cbrt
  275. JS_DEFINE_NATIVE_FUNCTION(MathObject::cbrt)
  276. {
  277. // 1. Let n be ? ToNumber(x).
  278. auto number = TRY(vm.argument(0).to_number(vm));
  279. // 2. If n is not finite or n is either +0๐”ฝ or -0๐”ฝ, return n.
  280. if (!number.is_finite_number() || number.as_double() == 0)
  281. return number;
  282. // 3. Return an implementation-approximated Number value representing the result of the cube root of โ„(n).
  283. return Value(::cbrt(number.as_double()));
  284. }
  285. // 21.3.2.10 Math.ceil ( x ), https://tc39.es/ecma262/#sec-math.ceil
  286. ThrowCompletionOr<Value> MathObject::ceil_impl(VM& vm, Value x)
  287. {
  288. // 1. Let n be ? ToNumber(x).
  289. auto number = TRY(x.to_number(vm));
  290. // 2. If n is not finite or n is either +0๐”ฝ or -0๐”ฝ, return n.
  291. if (!number.is_finite_number() || number.as_double() == 0)
  292. return number;
  293. // 3. If n < -0๐”ฝ and n > -1๐”ฝ, return -0๐”ฝ.
  294. if (number.as_double() < 0 && number.as_double() > -1)
  295. return Value(-0.f);
  296. // 4. If n is an integral Number, return n.
  297. // 5. Return the smallest (closest to -โˆž) integral Number value that is not less than n.
  298. return Value(::ceil(number.as_double()));
  299. }
  300. // 21.3.2.10 Math.ceil ( x ), https://tc39.es/ecma262/#sec-math.ceil
  301. JS_DEFINE_NATIVE_FUNCTION(MathObject::ceil)
  302. {
  303. return ceil_impl(vm, vm.argument(0));
  304. }
  305. // 21.3.2.11 Math.clz32 ( x ), https://tc39.es/ecma262/#sec-math.clz32
  306. JS_DEFINE_NATIVE_FUNCTION(MathObject::clz32)
  307. {
  308. // 1. Let n be ? ToUint32(x).
  309. auto number = TRY(vm.argument(0).to_u32(vm));
  310. // 2. Let p be the number of leading zero bits in the unsigned 32-bit binary representation of n.
  311. // 3. Return ๐”ฝ(p).
  312. return Value(count_leading_zeroes_safe(number));
  313. }
  314. // 21.3.2.12 Math.cos ( x ), https://tc39.es/ecma262/#sec-math.cos
  315. JS_DEFINE_NATIVE_FUNCTION(MathObject::cos)
  316. {
  317. // 1. Let n be ? ToNumber(x).
  318. auto number = TRY(vm.argument(0).to_number(vm));
  319. // 2. If n is NaN, n is +โˆž๐”ฝ, or n is -โˆž๐”ฝ, return NaN.
  320. if (number.is_nan() || number.is_infinity())
  321. return js_nan();
  322. // 3. If n is +0๐”ฝ or n is -0๐”ฝ, return 1๐”ฝ.
  323. if (number.is_positive_zero() || number.is_negative_zero())
  324. return Value(1);
  325. // 4. Return an implementation-approximated Number value representing the result of the cosine of โ„(n).
  326. return Value(::cos(number.as_double()));
  327. }
  328. // 21.3.2.13 Math.cosh ( x ), https://tc39.es/ecma262/#sec-math.cosh
  329. JS_DEFINE_NATIVE_FUNCTION(MathObject::cosh)
  330. {
  331. // 1. Let n be ? ToNumber(x).
  332. auto number = TRY(vm.argument(0).to_number(vm));
  333. // 2. If n is NaN, return NaN.
  334. if (number.is_nan())
  335. return js_nan();
  336. // 3. If n is +โˆž๐”ฝ or n is -โˆž๐”ฝ, return +โˆž๐”ฝ.
  337. if (number.is_positive_infinity() || number.is_negative_infinity())
  338. return js_infinity();
  339. // 4. If n is +0๐”ฝ or n is -0๐”ฝ, return 1๐”ฝ.
  340. if (number.is_positive_zero() || number.is_negative_zero())
  341. return Value(1);
  342. // 5. Return an implementation-approximated Number value representing the result of the hyperbolic cosine of โ„(n).
  343. return Value(::cosh(number.as_double()));
  344. }
  345. // 21.3.2.14 Math.exp ( x ), https://tc39.es/ecma262/#sec-math.exp
  346. ThrowCompletionOr<Value> MathObject::exp_impl(VM& vm, Value x)
  347. {
  348. // 1. Let n be ? ToNumber(x).
  349. auto number = TRY(x.to_number(vm));
  350. // 2. If n is either NaN or +โˆž๐”ฝ, return n.
  351. if (number.is_nan() || number.is_positive_infinity())
  352. return number;
  353. // 3. If n is either +0๐”ฝ or -0๐”ฝ, return 1๐”ฝ.
  354. if (number.as_double() == 0)
  355. return Value(1);
  356. // 4. If n is -โˆž๐”ฝ, return +0๐”ฝ.
  357. if (number.is_negative_infinity())
  358. return Value(0);
  359. // 5. Return an implementation-approximated Number value representing the result of the exponential function of โ„(n).
  360. return Value(::exp(number.as_double()));
  361. }
  362. // 21.3.2.14 Math.exp ( x ), https://tc39.es/ecma262/#sec-math.exp
  363. JS_DEFINE_NATIVE_FUNCTION(MathObject::exp)
  364. {
  365. return exp_impl(vm, vm.argument(0));
  366. }
  367. // 21.3.2.15 Math.expm1 ( x ), https://tc39.es/ecma262/#sec-math.expm1
  368. JS_DEFINE_NATIVE_FUNCTION(MathObject::expm1)
  369. {
  370. // 1. Let n be ? ToNumber(x).
  371. auto number = TRY(vm.argument(0).to_number(vm));
  372. // 2. If n is one of NaN, +0๐”ฝ, -0๐”ฝ, or +โˆž๐”ฝ, return n.
  373. if (number.is_nan() || number.as_double() == 0 || number.is_positive_infinity())
  374. return number;
  375. // 3. If n is -โˆž๐”ฝ, return -1๐”ฝ.
  376. if (number.is_negative_infinity())
  377. return Value(-1);
  378. // 4. Return an implementation-approximated Number value representing the result of subtracting 1 from the exponential function of โ„(n).
  379. return Value(::expm1(number.as_double()));
  380. }
  381. // 21.3.2.16 Math.floor ( x ), https://tc39.es/ecma262/#sec-math.floor
  382. ThrowCompletionOr<Value> MathObject::floor_impl(VM& vm, Value x)
  383. {
  384. // 1. Let n be ? ToNumber(x).
  385. auto number = TRY(x.to_number(vm));
  386. // 2. If n is not finite or n is either +0๐”ฝ or -0๐”ฝ, return n.
  387. if (!number.is_finite_number() || number.as_double() == 0)
  388. return number;
  389. // 3. If n < 1๐”ฝ and n > +0๐”ฝ, return +0๐”ฝ.
  390. // 4. If n is an integral Number, return n.
  391. // 5. Return the greatest (closest to +โˆž) integral Number value that is not greater than n.
  392. return Value(::floor(number.as_double()));
  393. }
  394. // 21.3.2.16 Math.floor ( x ), https://tc39.es/ecma262/#sec-math.floor
  395. JS_DEFINE_NATIVE_FUNCTION(MathObject::floor)
  396. {
  397. return floor_impl(vm, vm.argument(0));
  398. }
  399. // 21.3.2.17 Math.fround ( x ), https://tc39.es/ecma262/#sec-math.fround
  400. JS_DEFINE_NATIVE_FUNCTION(MathObject::fround)
  401. {
  402. // 1. Let n be ? ToNumber(x).
  403. auto number = TRY(vm.argument(0).to_number(vm));
  404. // 2. If n is NaN, return NaN.
  405. if (number.is_nan())
  406. return js_nan();
  407. // 3. If n is one of +0๐”ฝ, -0๐”ฝ, +โˆž๐”ฝ, or -โˆž๐”ฝ, return n.
  408. if (number.as_double() == 0 || number.is_infinity())
  409. return number;
  410. // 4. Let n32 be the result of converting n to a value in IEEE 754-2019 binary32 format using roundTiesToEven mode.
  411. // 5. Let n64 be the result of converting n32 to a value in IEEE 754-2019 binary64 format.
  412. // 6. Return the ECMAScript Number value corresponding to n64.
  413. return Value((float)number.as_double());
  414. }
  415. // 3.1 Math.f16round ( x ), https://tc39.es/proposal-float16array/#sec-math.f16round
  416. JS_DEFINE_NATIVE_FUNCTION(MathObject::f16round)
  417. {
  418. // 1. Let n be ? ToNumber(x).
  419. auto number = TRY(vm.argument(0).to_number(vm));
  420. // 2. If n is NaN, return NaN.
  421. if (number.is_nan())
  422. return js_nan();
  423. // 3. If n is one of +0๐”ฝ, -0๐”ฝ, +โˆž๐”ฝ, or -โˆž๐”ฝ, return n.
  424. if (number.as_double() == 0 || number.is_infinity())
  425. return number;
  426. // 4. Let n16 be the result of converting n to IEEE 754-2019 binary16 format using roundTiesToEven mode.
  427. // 5. Let n64 be the result of converting n16 to IEEE 754-2019 binary64 format.
  428. // 6. Return the ECMAScript Number value corresponding to n64.
  429. return Value(static_cast<f16>(number.as_double()));
  430. }
  431. // 21.3.2.18 Math.hypot ( ...args ), https://tc39.es/ecma262/#sec-math.hypot
  432. JS_DEFINE_NATIVE_FUNCTION(MathObject::hypot)
  433. {
  434. // 1. Let coerced be a new empty List.
  435. Vector<Value> coerced;
  436. // 2. For each element arg of args, do
  437. for (size_t i = 0; i < vm.argument_count(); ++i) {
  438. // a. Let n be ? ToNumber(arg).
  439. auto number = TRY(vm.argument(i).to_number(vm));
  440. // b. Append n to coerced.
  441. coerced.append(number);
  442. }
  443. // 3. For each element number of coerced, do
  444. for (auto& number : coerced) {
  445. // a. If number is either +โˆž๐”ฝ or -โˆž๐”ฝ, return +โˆž๐”ฝ.
  446. if (number.is_infinity())
  447. return js_infinity();
  448. }
  449. // 4. Let onlyZero be true.
  450. auto only_zero = true;
  451. double sum_of_squares = 0;
  452. // 5. For each element number of coerced, do
  453. for (auto& number : coerced) {
  454. // a. If number is NaN, return NaN.
  455. // OPTIMIZATION: For infinities, the result will be infinity with the same sign, so we can return early.
  456. if (number.is_nan() || number.is_infinity())
  457. return number;
  458. // b. If number is neither +0๐”ฝ nor -0๐”ฝ, set onlyZero to false.
  459. if (number.as_double() != 0)
  460. only_zero = false;
  461. sum_of_squares += number.as_double() * number.as_double();
  462. }
  463. // 6. If onlyZero is true, return +0๐”ฝ.
  464. if (only_zero)
  465. return Value(0);
  466. // 7. Return an implementation-approximated Number value representing the square root of the sum of squares of the mathematical values of the elements of coerced.
  467. return Value(::sqrt(sum_of_squares));
  468. }
  469. // 21.3.2.19 Math.imul ( x, y ), https://tc39.es/ecma262/#sec-math.imul
  470. JS_DEFINE_NATIVE_FUNCTION(MathObject::imul)
  471. {
  472. // 1. Let a be โ„(? ToUint32(x)).
  473. auto a = TRY(vm.argument(0).to_u32(vm));
  474. // 2. Let b be โ„(? ToUint32(y)).
  475. auto b = TRY(vm.argument(1).to_u32(vm));
  476. // 3. Let product be (a ร— b) modulo 2^32.
  477. // 4. If product โ‰ฅ 2^31, return ๐”ฝ(product - 2^32); otherwise return ๐”ฝ(product).
  478. return Value(static_cast<i32>(a * b));
  479. }
  480. // 21.3.2.20 Math.log ( x ), https://tc39.es/ecma262/#sec-math.log
  481. ThrowCompletionOr<Value> MathObject::log_impl(VM& vm, Value x)
  482. {
  483. // 1. Let n be ? ToNumber(x).
  484. auto number = TRY(x.to_number(vm));
  485. // 2. If n is NaN or n is +โˆž๐”ฝ, return n.
  486. if (number.is_nan() || number.is_positive_infinity())
  487. return number;
  488. // 3. If n is 1๐”ฝ, return +0๐”ฝ.
  489. if (number.as_double() == 1.)
  490. return Value(0);
  491. // 4. If n is +0๐”ฝ or n is -0๐”ฝ, return -โˆž๐”ฝ.
  492. if (number.is_positive_zero() || number.is_negative_zero())
  493. return js_negative_infinity();
  494. // 5. If n < -0๐”ฝ, return NaN.
  495. if (number.as_double() < -0.)
  496. return js_nan();
  497. // 6. Return an implementation-approximated Number value representing the result of the natural logarithm of โ„(n).
  498. return Value(::log(number.as_double()));
  499. }
  500. // 21.3.2.20 Math.log ( x ), https://tc39.es/ecma262/#sec-math.log
  501. JS_DEFINE_NATIVE_FUNCTION(MathObject::log)
  502. {
  503. return log_impl(vm, vm.argument(0));
  504. }
  505. // 21.3.2.21 Math.log1p ( x ), https://tc39.es/ecma262/#sec-math.log1p
  506. JS_DEFINE_NATIVE_FUNCTION(MathObject::log1p)
  507. {
  508. // 1. Let n be ? ToNumber(x).
  509. auto number = TRY(vm.argument(0).to_number(vm));
  510. // 2. If n is NaN, n is +0๐”ฝ, n is -0๐”ฝ, or n is +โˆž๐”ฝ, return n.
  511. if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero() || number.is_positive_infinity())
  512. return number;
  513. // 3. If n is -1๐”ฝ, return -โˆž๐”ฝ.
  514. if (number.as_double() == -1.)
  515. return js_negative_infinity();
  516. // 4. If n < -1๐”ฝ, return NaN.
  517. if (number.as_double() < -1.)
  518. return js_nan();
  519. // 5. Return an implementation-approximated Number value representing the result of the natural logarithm of 1 + โ„(n).
  520. return Value(::log1p(number.as_double()));
  521. }
  522. // 21.3.2.22 Math.log10 ( x ), https://tc39.es/ecma262/#sec-math.log10
  523. JS_DEFINE_NATIVE_FUNCTION(MathObject::log10)
  524. {
  525. // 1. Let n be ? ToNumber(x).
  526. auto number = TRY(vm.argument(0).to_number(vm));
  527. // 2. If n is NaN or n is +โˆž๐”ฝ, return n.
  528. if (number.is_nan() || number.is_positive_infinity())
  529. return number;
  530. // 3. If n is 1๐”ฝ, return +0๐”ฝ.
  531. if (number.as_double() == 1.)
  532. return Value(0);
  533. // 4. If n is +0๐”ฝ or n is -0๐”ฝ, return -โˆž๐”ฝ.
  534. if (number.is_positive_zero() || number.is_negative_zero())
  535. return js_negative_infinity();
  536. // 5. If n < -0๐”ฝ, return NaN.
  537. if (number.as_double() < -0.)
  538. return js_nan();
  539. // 6. Return an implementation-approximated Number value representing the result of the base 10 logarithm of โ„(n).
  540. return Value(::log10(number.as_double()));
  541. }
  542. // 21.3.2.23 Math.log2 ( x ), https://tc39.es/ecma262/#sec-math.log2
  543. JS_DEFINE_NATIVE_FUNCTION(MathObject::log2)
  544. {
  545. // 1. Let n be ? ToNumber(x).
  546. auto number = TRY(vm.argument(0).to_number(vm));
  547. // 2. If n is NaN or n is +โˆž๐”ฝ, return n.
  548. if (number.is_nan() || number.is_positive_infinity())
  549. return number;
  550. // 3. If n is 1๐”ฝ, return +0๐”ฝ.
  551. if (number.as_double() == 1.)
  552. return Value(0);
  553. // 4. If n is +0๐”ฝ or n is -0๐”ฝ, return -โˆž๐”ฝ.
  554. if (number.is_positive_zero() || number.is_negative_zero())
  555. return js_negative_infinity();
  556. // 5. If n < -0๐”ฝ, return NaN.
  557. if (number.as_double() < -0.)
  558. return js_nan();
  559. // 6. Return an implementation-approximated Number value representing the result of the base 2 logarithm of โ„(n).
  560. return Value(::log2(number.as_double()));
  561. }
  562. // 21.3.2.24 Math.max ( ...args ), https://tc39.es/ecma262/#sec-math.max
  563. JS_DEFINE_NATIVE_FUNCTION(MathObject::max)
  564. {
  565. // 1. Let coerced be a new empty List.
  566. Vector<Value> coerced;
  567. // 2. For each element arg of args, do
  568. for (size_t i = 0; i < vm.argument_count(); ++i) {
  569. // a. Let n be ? ToNumber(arg).
  570. auto number = TRY(vm.argument(i).to_number(vm));
  571. // b. Append n to coerced.
  572. coerced.append(number);
  573. }
  574. // 3. Let highest be -โˆž๐”ฝ.
  575. auto highest = js_negative_infinity();
  576. // 4. For each element number of coerced, do
  577. for (auto& number : coerced) {
  578. // a. If number is NaN, return NaN.
  579. if (number.is_nan())
  580. return js_nan();
  581. // b. If number is +0๐”ฝ and highest is -0๐”ฝ, set highest to +0๐”ฝ.
  582. // c. If number > highest, set highest to number.
  583. if ((number.is_positive_zero() && highest.is_negative_zero()) || number.as_double() > highest.as_double())
  584. highest = number;
  585. }
  586. // 5. Return highest.
  587. return highest;
  588. }
  589. // 21.3.2.25 Math.min ( ...args ), https://tc39.es/ecma262/#sec-math.min
  590. JS_DEFINE_NATIVE_FUNCTION(MathObject::min)
  591. {
  592. // 1. Let coerced be a new empty List.
  593. Vector<Value> coerced;
  594. // 2. For each element arg of args, do
  595. for (size_t i = 0; i < vm.argument_count(); ++i) {
  596. // a. Let n be ? ToNumber(arg).
  597. auto number = TRY(vm.argument(i).to_number(vm));
  598. // b. Append n to coerced.
  599. coerced.append(number);
  600. }
  601. // 3. Let lowest be +โˆž๐”ฝ.
  602. auto lowest = js_infinity();
  603. // 4. For each element number of coerced, do
  604. for (auto& number : coerced) {
  605. // a. If number is NaN, return NaN.
  606. if (number.is_nan())
  607. return js_nan();
  608. // b. If number is -0๐”ฝ and lowest is +0๐”ฝ, set lowest to -0๐”ฝ.
  609. // c. If number < lowest, set lowest to number.
  610. if ((number.is_negative_zero() && lowest.is_positive_zero()) || number.as_double() < lowest.as_double())
  611. lowest = number;
  612. }
  613. // 5. Return lowest.
  614. return lowest;
  615. }
  616. // 21.3.2.26 Math.pow ( base, exponent ), https://tc39.es/ecma262/#sec-math.pow
  617. ThrowCompletionOr<Value> MathObject::pow_impl(VM& vm, Value base, Value exponent)
  618. {
  619. // Set base to ? ToNumber(base).
  620. base = TRY(base.to_number(vm));
  621. // 2. Set exponent to ? ToNumber(exponent).
  622. exponent = TRY(exponent.to_number(vm));
  623. // 3. Return Number::exponentiate(base, exponent).
  624. return JS::exp(vm, base, exponent);
  625. }
  626. // 21.3.2.26 Math.pow ( base, exponent ), https://tc39.es/ecma262/#sec-math.pow
  627. JS_DEFINE_NATIVE_FUNCTION(MathObject::pow)
  628. {
  629. return pow_impl(vm, vm.argument(0), vm.argument(1));
  630. }
  631. // 21.3.2.27 Math.random ( ), https://tc39.es/ecma262/#sec-math.random
  632. JS_DEFINE_NATIVE_FUNCTION(MathObject::random)
  633. {
  634. // This function returns a Number value with positive sign, greater than or equal to +0๐”ฝ but strictly less than 1๐”ฝ,
  635. // chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an
  636. // implementation-defined algorithm or strategy.
  637. double r = (double)get_random<u32>() / (double)UINT32_MAX;
  638. return Value(r);
  639. }
  640. // 21.3.2.28 Math.round ( x ), https://tc39.es/ecma262/#sec-math.round
  641. ThrowCompletionOr<Value> MathObject::round_impl(VM& vm, Value x)
  642. {
  643. // 1. Let n be ? ToNumber(x).
  644. auto number = TRY(x.to_number(vm));
  645. // 2. If n is not finite or n is an integral Number, return n.
  646. if (!number.is_finite_number() || number.as_double() == ::trunc(number.as_double()))
  647. return number;
  648. // 3. If n < 0.5๐”ฝ and n > +0๐”ฝ, return +0๐”ฝ.
  649. // 4. If n < -0๐”ฝ and n โ‰ฅ -0.5๐”ฝ, return -0๐”ฝ.
  650. // 5. Return the integral Number closest to n, preferring the Number closer to +โˆž in the case of a tie.
  651. double integer = ::ceil(number.as_double());
  652. if (integer - 0.5 > number.as_double())
  653. integer--;
  654. return Value(integer);
  655. }
  656. // 21.3.2.28 Math.round ( x ), https://tc39.es/ecma262/#sec-math.round
  657. JS_DEFINE_NATIVE_FUNCTION(MathObject::round)
  658. {
  659. return round_impl(vm, vm.argument(0));
  660. }
  661. // 21.3.2.29 Math.sign ( x ), https://tc39.es/ecma262/#sec-math.sign
  662. JS_DEFINE_NATIVE_FUNCTION(MathObject::sign)
  663. {
  664. // 1. Let n be ? ToNumber(x).
  665. auto number = TRY(vm.argument(0).to_number(vm));
  666. // 2. If n is one of NaN, +0๐”ฝ, or -0๐”ฝ, return n.
  667. if (number.is_nan() || number.as_double() == 0)
  668. return number;
  669. // 3. If n < -0๐”ฝ, return -1๐”ฝ.
  670. if (number.as_double() < 0)
  671. return Value(-1);
  672. // 4. Return 1๐”ฝ.
  673. return Value(1);
  674. }
  675. // 21.3.2.30 Math.sin ( x ), https://tc39.es/ecma262/#sec-math.sin
  676. JS_DEFINE_NATIVE_FUNCTION(MathObject::sin)
  677. {
  678. // 1. Let n be ? ToNumber(x).
  679. auto number = TRY(vm.argument(0).to_number(vm));
  680. // 2. If n is NaN, n is +0๐”ฝ, or n is -0๐”ฝ, return n.
  681. if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
  682. return number;
  683. // 3. If n is +โˆž๐”ฝ or n is -โˆž๐”ฝ, return NaN.
  684. if (number.is_infinity())
  685. return js_nan();
  686. // 4. Return an implementation-approximated Number value representing the result of the sine of โ„(n).
  687. return Value(::sin(number.as_double()));
  688. }
  689. // 21.3.2.31 Math.sinh ( x ), https://tc39.es/ecma262/#sec-math.sinh
  690. JS_DEFINE_NATIVE_FUNCTION(MathObject::sinh)
  691. {
  692. // 1. Let n be ? ToNumber(x).
  693. auto number = TRY(vm.argument(0).to_number(vm));
  694. // 2. If n is not finite or n is either +0๐”ฝ or -0๐”ฝ, return n.
  695. if (!number.is_finite_number() || number.is_positive_zero() || number.is_negative_zero())
  696. return number;
  697. // 3. Return an implementation-approximated Number value representing the result of the hyperbolic sine of โ„(n).
  698. return Value(::sinh(number.as_double()));
  699. }
  700. // 21.3.2.32 Math.sqrt ( x ), https://tc39.es/ecma262/#sec-math.sqrt
  701. ThrowCompletionOr<Value> MathObject::sqrt_impl(VM& vm, Value x)
  702. {
  703. // Let n be ? ToNumber(x).
  704. auto number = TRY(x.to_number(vm));
  705. // 2. If n is one of NaN, +0๐”ฝ, -0๐”ฝ, or +โˆž๐”ฝ, return n.
  706. if (number.is_nan() || number.as_double() == 0 || number.is_positive_infinity())
  707. return number;
  708. // 3. If n < -0๐”ฝ, return NaN.
  709. if (number.as_double() < 0)
  710. return js_nan();
  711. // 4. Return an implementation-approximated Number value representing the result of the square root of โ„(n).
  712. return Value(::sqrt(number.as_double()));
  713. }
  714. // 21.3.2.32 Math.sqrt ( x ), https://tc39.es/ecma262/#sec-math.sqrt
  715. JS_DEFINE_NATIVE_FUNCTION(MathObject::sqrt)
  716. {
  717. return sqrt_impl(vm, vm.argument(0));
  718. }
  719. // 21.3.2.33 Math.tan ( x ), https://tc39.es/ecma262/#sec-math.tan
  720. JS_DEFINE_NATIVE_FUNCTION(MathObject::tan)
  721. {
  722. // Let n be ? ToNumber(x).
  723. auto number = TRY(vm.argument(0).to_number(vm));
  724. // 2. If n is NaN, n is +0๐”ฝ, or n is -0๐”ฝ, return n.
  725. if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
  726. return number;
  727. // 3. If n is +โˆž๐”ฝ, or n is -โˆž๐”ฝ, return NaN.
  728. if (number.is_infinity())
  729. return js_nan();
  730. // 4. Return an implementation-approximated Number value representing the result of the tangent of โ„(n).
  731. return Value(::tan(number.as_double()));
  732. }
  733. // 21.3.2.34 Math.tanh ( x ), https://tc39.es/ecma262/#sec-math.tanh
  734. JS_DEFINE_NATIVE_FUNCTION(MathObject::tanh)
  735. {
  736. // 1. Let n be ? ToNumber(x).
  737. auto number = TRY(vm.argument(0).to_number(vm));
  738. // 2. If n is NaN, n is +0๐”ฝ, or n is -0๐”ฝ, return n.
  739. if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
  740. return number;
  741. // 3. If n is +โˆž๐”ฝ, return 1๐”ฝ.
  742. if (number.is_positive_infinity())
  743. return Value(1);
  744. // 4. If n is -โˆž๐”ฝ, return -1๐”ฝ.
  745. if (number.is_negative_infinity())
  746. return Value(-1);
  747. // 5. Return an implementation-approximated Number value representing the result of the hyperbolic tangent of โ„(n).
  748. return Value(::tanh(number.as_double()));
  749. }
  750. // 21.3.2.35 Math.trunc ( x ), https://tc39.es/ecma262/#sec-math.trunc
  751. JS_DEFINE_NATIVE_FUNCTION(MathObject::trunc)
  752. {
  753. // 1. Let n be ? ToNumber(x).
  754. auto number = TRY(vm.argument(0).to_number(vm));
  755. // 2. If n is not finite or n is either +0๐”ฝ or -0๐”ฝ, return n.
  756. if (number.is_nan() || number.is_infinity() || number.as_double() == 0)
  757. return number;
  758. // 3. If n < 1๐”ฝ and n > +0๐”ฝ, return +0๐”ฝ.
  759. // 4. If n < -0๐”ฝ and n > -1๐”ฝ, return -0๐”ฝ.
  760. // 5. Return the integral Number nearest n in the direction of +0๐”ฝ.
  761. return Value(number.as_double() < 0
  762. ? ::ceil(number.as_double())
  763. : ::floor(number.as_double()));
  764. }
  765. }