boa_engine/builtins/math/mod.rs
1//! Boa's implementation of ECMAScript's global `Math` object.
2//!
3//! `Math` is a built-in object that has properties and methods for mathematical constants and functions. Itβs not a function object.
4//!
5//! `Math` works with the `Number` type. It doesn't work with `BigInt`.
6//!
7//! More information:
8//! - [ECMAScript reference][spec]
9//! - [MDN documentation][mdn]
10//!
11//! [spec]: https://tc39.es/ecma262/#sec-math-object
12//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
13
14use crate::{
15 Context, JsArgs, JsResult, JsString, JsValue,
16 builtins::BuiltInObject,
17 context::intrinsics::Intrinsics,
18 js_string,
19 native_function::{NativeFunction, NativeFunctionPointer},
20 object::JsObject,
21 property::Attribute,
22 realm::Realm,
23 string::StaticJsStrings,
24 symbol::JsSymbol,
25};
26
27use super::{BuiltInBuilder, IntrinsicObject};
28
29#[cfg(test)]
30mod tests;
31
32/// Javascript `Math` object.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
34pub(crate) struct Math;
35
36impl IntrinsicObject for Math {
37 fn init(realm: &Realm) {
38 let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
39 let builder = BuiltInBuilder::with_intrinsic::<Self>(realm)
40 .static_property(js_string!("E"), std::f64::consts::E, attribute)
41 .static_property(js_string!("LN10"), std::f64::consts::LN_10, attribute)
42 .static_property(js_string!("LN2"), std::f64::consts::LN_2, attribute)
43 .static_property(js_string!("LOG10E"), std::f64::consts::LOG10_E, attribute)
44 .static_property(js_string!("LOG2E"), std::f64::consts::LOG2_E, attribute)
45 .static_property(js_string!("PI"), std::f64::consts::PI, attribute)
46 .static_property(
47 js_string!("SQRT1_2"),
48 std::f64::consts::FRAC_1_SQRT_2,
49 attribute,
50 )
51 .static_property(js_string!("SQRT2"), std::f64::consts::SQRT_2, attribute)
52 .static_method(Self::abs, js_string!("abs"), 1)
53 .static_method(Self::acos, js_string!("acos"), 1)
54 .static_method(Self::acosh, js_string!("acosh"), 1)
55 .static_method(Self::asin, js_string!("asin"), 1)
56 .static_method(Self::asinh, js_string!("asinh"), 1)
57 .static_method(Self::atan, js_string!("atan"), 1)
58 .static_method(Self::atanh, js_string!("atanh"), 1)
59 .static_method(Self::atan2, js_string!("atan2"), 2)
60 .static_method(Self::cbrt, js_string!("cbrt"), 1)
61 .static_method(Self::ceil, js_string!("ceil"), 1)
62 .static_method(Self::clz32, js_string!("clz32"), 1)
63 .static_method(Self::cos, js_string!("cos"), 1)
64 .static_method(Self::cosh, js_string!("cosh"), 1)
65 .static_method(Self::exp, js_string!("exp"), 1)
66 .static_method(Self::expm1, js_string!("expm1"), 1)
67 .static_method(Self::floor, js_string!("floor"), 1)
68 .static_method(Self::fround, js_string!("fround"), 1)
69 .static_method(Self::hypot, js_string!("hypot"), 2)
70 .static_method(Self::imul, js_string!("imul"), 2)
71 .static_method(Self::log, js_string!("log"), 1)
72 .static_method(Self::log1p, js_string!("log1p"), 1)
73 .static_method(Self::log10, js_string!("log10"), 1)
74 .static_method(Self::log2, js_string!("log2"), 1)
75 .static_method(Self::max, js_string!("max"), 2)
76 .static_method(Self::min, js_string!("min"), 2)
77 .static_method(Self::pow, js_string!("pow"), 2)
78 .static_method(Self::random, js_string!("random"), 0)
79 .static_method(Self::round, js_string!("round"), 1)
80 .static_method(Self::sign, js_string!("sign"), 1)
81 .static_method(Self::sin, js_string!("sin"), 1)
82 .static_method(Self::sinh, js_string!("sinh"), 1)
83 .static_method(Self::sqrt, js_string!("sqrt"), 1)
84 .static_method(Self::tan, js_string!("tan"), 1)
85 .static_method(Self::tanh, js_string!("tanh"), 1)
86 .static_method(Self::trunc, js_string!("trunc"), 1)
87 .static_property(
88 JsSymbol::to_string_tag(),
89 Self::NAME,
90 Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
91 );
92
93 #[cfg(feature = "float16")]
94 let builder = builder.static_method(Self::f16round, js_string!("f16round"), 1);
95
96 #[cfg(feature = "xsum")]
97 let builder = builder.static_method(Self::sum_precise, js_string!("sumPrecise"), 1);
98
99 builder.build();
100 }
101
102 fn get(intrinsics: &Intrinsics) -> JsObject {
103 intrinsics.objects().math()
104 }
105}
106
107impl BuiltInObject for Math {
108 const NAME: JsString = StaticJsStrings::MATH;
109}
110
111/// A Math function on Number arguments only, which cannot run user code.
112type NumberFn = fn(&[f64]) -> f64;
113
114impl Math {
115 /// The common Math functions that, given only numbers, run no user code and cannot
116 /// throw. A `CallMath` function id is an index into this table.
117 ///
118 /// The third field computes the same result from Number arguments directly, where the
119 /// intrinsic is that one f64 operation; `None` sends the call to the intrinsic.
120 const PURE: [(&'static str, NativeFunctionPointer, Option<NumberFn>); 17] = [
121 (
122 "max",
123 Math::max,
124 Some(|n| {
125 n.iter()
126 .fold(f64::NEG_INFINITY, |h, &x| Self::max_step(h, x))
127 }),
128 ),
129 (
130 "min",
131 Math::min,
132 Some(|n| n.iter().fold(f64::INFINITY, |l, &x| Self::min_step(l, x))),
133 ),
134 (
135 "floor",
136 Math::floor,
137 Some(|n| n.first().map_or(f64::NAN, |x| x.floor())),
138 ),
139 (
140 "ceil",
141 Math::ceil,
142 Some(|n| n.first().map_or(f64::NAN, |x| x.ceil())),
143 ),
144 (
145 "abs",
146 Math::abs,
147 Some(|n| n.first().map_or(f64::NAN, |x| x.abs())),
148 ),
149 (
150 "sqrt",
151 Math::sqrt,
152 Some(|n| n.first().map_or(f64::NAN, |x| x.sqrt())),
153 ),
154 (
155 "sin",
156 Math::sin,
157 Some(|n| n.first().map_or(f64::NAN, |x| x.sin())),
158 ),
159 (
160 "cos",
161 Math::cos,
162 Some(|n| n.first().map_or(f64::NAN, |x| x.cos())),
163 ),
164 ("round", Math::round, None),
165 (
166 "trunc",
167 Math::trunc,
168 Some(|n| n.first().map_or(f64::NAN, |x| x.trunc())),
169 ),
170 ("atan2", Math::atan2, None),
171 ("hypot", Math::hypot, None),
172 (
173 "exp",
174 Math::exp,
175 Some(|n| n.first().map_or(f64::NAN, |x| x.exp())),
176 ),
177 ("pow", Math::pow, None),
178 ("sign", Math::sign, None),
179 (
180 "log",
181 Math::log,
182 Some(|n| n.first().map_or(f64::NAN, |x| x.ln())),
183 ),
184 ("imul", Math::imul, None),
185 ];
186
187 /// One step of `Math.max`: `NaN` sticks, and +0 is above -0.
188 fn max_step(highest: f64, num: f64) -> f64 {
189 if highest.is_nan() {
190 highest
191 } else if num.is_nan() {
192 f64::NAN
193 } else if highest == 0f64 && num == 0f64 && highest.signum() != num.signum() {
194 0f64
195 } else {
196 highest.max(num)
197 }
198 }
199
200 /// One step of `Math.min`: `NaN` sticks, and -0 is below +0.
201 fn min_step(lowest: f64, num: f64) -> f64 {
202 if lowest.is_nan() {
203 lowest
204 } else if num.is_nan() {
205 f64::NAN
206 } else if lowest == 0f64 && num == 0f64 && lowest.signum() != num.signum() {
207 -0f64
208 } else {
209 lowest.min(num)
210 }
211 }
212
213 /// `CallMath` function `id` on Number arguments, when it has a direct form.
214 pub(crate) fn call_pure_numbers(id: u8, args: &[f64]) -> Option<f64> {
215 Some((Self::PURE[usize::from(id)].2?)(args))
216 }
217
218 /// Number of `CallMath` function ids. Only the bytecode verifier reads it.
219 #[cfg(debug_assertions)]
220 pub(crate) const PURE_COUNT: usize = Self::PURE.len();
221
222 /// Returns `true` if `f` is one of the common Math functions that, given only numbers, run
223 /// no user code and cannot throw.
224 pub(crate) fn is_pure_on_numbers(f: &NativeFunction) -> bool {
225 Self::PURE.iter().any(|&(_, pure, _)| f.is_fn_ptr(pure))
226 }
227
228 /// The `CallMath` function id of `Math.<name>`, if it is one of [`Self::PURE`].
229 pub(crate) fn pure_id(name: &JsString) -> Option<u8> {
230 Self::PURE
231 .iter()
232 .position(|&(candidate, _, _)| name == candidate)
233 .and_then(|index| u8::try_from(index).ok())
234 }
235
236 /// Property name of `CallMath` function `id`.
237 pub(crate) fn pure_name(id: u8) -> JsString {
238 JsString::from(Self::PURE[usize::from(id)].0)
239 }
240
241 /// Calls `CallMath` function `id` with `args`. Every function in [`Self::PURE`]
242 /// ignores `this`.
243 pub(crate) fn call_pure(id: u8, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
244 (Self::PURE[usize::from(id)].1)(&JsValue::undefined(), args, context)
245 }
246
247 /// Get the absolute value of a number.
248 ///
249 /// More information:
250 /// - [ECMAScript reference][spec]
251 /// - [MDN documentation][mdn]
252 ///
253 /// [spec]: https://tc39.es/ecma262/#sec-math.abs
254 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs
255 pub(crate) fn abs(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
256 Ok(args
257 .get_or_undefined(0)
258 // 1. Let n be ? ToNumber(x).
259 .to_number(context)?
260 // 3. If n is -0π½, return +0π½.
261 // 2. If n is NaN, return NaN.
262 // 4. If n is -βπ½, return +βπ½.
263 // 5. If n < +0π½, return -n.
264 // 6. Return n.
265 .abs()
266 .into())
267 }
268
269 /// Get the arccos of a number.
270 ///
271 /// More information:
272 /// - [ECMAScript reference][spec]
273 /// - [MDN documentation][mdn]
274 ///
275 /// [spec]: https://tc39.es/ecma262/#sec-math.acos
276 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos
277 pub(crate) fn acos(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
278 Ok(args
279 .get_or_undefined(0)
280 // 1. Let n be ? ToNumber(x).
281 .to_number(context)?
282 // 2. If n is NaN, n > 1π½, or n < -1π½, return NaN.
283 // 3. If n is 1π½, return +0π½.
284 // 4. Return an implementation-approximated value representing the result of the inverse cosine of β(n).
285 .acos()
286 .into())
287 }
288
289 /// Get the hyperbolic arccos of a number.
290 ///
291 /// More information:
292 /// - [ECMAScript reference][spec]
293 /// - [MDN documentation][mdn]
294 ///
295 /// [spec]: https://tc39.es/ecma262/#sec-math.acosh
296 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh
297 pub(crate) fn acosh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
298 // 1/βf64::EPSILON, as established by the Boost math library.
299 const ACOSH_LARGE_INPUT_THRESHOLD: f64 = 67_108_864.0;
300
301 // 1. Let n be ? ToNumber(x).
302 let n = args.get_or_undefined(0).to_number(context)?;
303 if n.is_finite() && n > ACOSH_LARGE_INPUT_THRESHOLD {
304 return Ok((n.ln() + std::f64::consts::LN_2).into());
305 }
306
307 // 4. If n < 1π½, return NaN.
308 // 2. If n is NaN or n is +βπ½, return n.
309 // 3. If n is 1π½, return +0π½.
310 // 5. Return an implementation-approximated value representing the result of the inverse hyperbolic cosine of β(n).
311 Ok(n.acosh().into())
312 }
313
314 /// Get the arcsine of a number.
315 ///
316 /// More information:
317 /// - [ECMAScript reference][spec]
318 /// - [MDN documentation][mdn]
319 ///
320 /// [spec]: https://tc39.es/ecma262/#sec-math.asin
321 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin
322 pub(crate) fn asin(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
323 Ok(args
324 .get_or_undefined(0)
325 // 1. Let n be ? ToNumber(x).
326 .to_number(context)?
327 // 2. If n is NaN, n is +0π½, or n is -0π½, return n.
328 // 3. If n > 1π½ or n < -1π½, return NaN.
329 // 4. Return an implementation-approximated value representing the result of the inverse sine of β(n).
330 .asin()
331 .into())
332 }
333
334 /// Get the hyperbolic arcsine of a number.
335 ///
336 /// More information:
337 /// - [ECMAScript reference][spec]
338 /// - [MDN documentation][mdn]
339 ///
340 /// [spec]: https://tc39.es/ecma262/#sec-math.asinh
341 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh
342 pub(crate) fn asinh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
343 // 1/βf64::EPSILON, as established by the Boost math library.
344 const ASINH_LARGE_INPUT_THRESHOLD: f64 = 67_108_864.0;
345
346 // 1. Let n be ? ToNumber(x).
347 let n = args.get_or_undefined(0).to_number(context)?;
348 if n.is_finite() && n.abs() > ASINH_LARGE_INPUT_THRESHOLD {
349 return Ok((n.signum()
350 * (n.abs().ln() + std::f64::consts::LN_2 + 0.25 * n.recip() * n.recip()))
351 .into());
352 }
353
354 // 2. If n is NaN, n is +0π½, n is -0π½, n is +βπ½, or n is -βπ½, return n.
355 // 3. Return an implementation-approximated value representing the result of the inverse hyperbolic sine of β(n).
356 Ok(n.asinh().into())
357 }
358
359 /// Get the arctangent of a number.
360 ///
361 /// More information:
362 /// - [ECMAScript reference][spec]
363 /// - [MDN documentation][mdn]
364 ///
365 /// [spec]: https://tc39.es/ecma262/#sec-math.atan
366 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan
367 pub(crate) fn atan(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
368 Ok(args
369 .get_or_undefined(0)
370 // 1. Let n be ? ToNumber(x).
371 .to_number(context)?
372 // 2. If n is NaN, n is +0π½, or n is -0π½, return n.
373 // 3. If n is +βπ½, return an implementation-approximated value representing Ο / 2.
374 // 4. If n is -βπ½, return an implementation-approximated value representing -Ο / 2.
375 // 5. Return an implementation-approximated value representing the result of the inverse tangent of β(n).
376 .atan()
377 .into())
378 }
379
380 /// Get the hyperbolic arctangent of a number.
381 ///
382 /// More information:
383 /// - [ECMAScript reference][spec]
384 /// - [MDN documentation][mdn]
385 ///
386 /// [spec]: https://tc39.es/ecma262/#sec-math.atanh
387 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh
388 pub(crate) fn atanh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
389 Ok(args
390 .get_or_undefined(0)
391 // 1. Let n be ? ToNumber(x).
392 .to_number(context)?
393 // 2. If n is NaN, n is +0π½, or n is -0π½, return n.
394 // 3. If n > 1π½ or n < -1π½, return NaN.
395 // 4. If n is 1π½, return +βπ½.
396 // 5. If n is -1π½, return -βπ½.
397 // 6. Return an implementation-approximated value representing the result of the inverse hyperbolic tangent of β(n).
398 .atanh()
399 .into())
400 }
401
402 /// Get the four quadrant arctangent of the quotient y / x.
403 ///
404 /// More information:
405 /// - [ECMAScript reference][spec]
406 /// - [MDN documentation][mdn]
407 ///
408 /// [spec]: https://tc39.es/ecma262/#sec-math.atan2
409 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
410 pub(crate) fn atan2(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
411 // 1. Let ny be ? ToNumber(y).
412 let y = args.get_or_undefined(0).to_number(context)?;
413
414 // 2. Let nx be ? ToNumber(x).
415 let x = args.get_or_undefined(1).to_number(context)?;
416
417 // 4. If ny is +βπ½, then
418 // a. If nx is +βπ½, return an implementation-approximated value representing Ο / 4.
419 // b. If nx is -βπ½, return an implementation-approximated value representing 3Ο / 4.
420 // c. Return an implementation-approximated value representing Ο / 2.
421 // 5. If ny is -βπ½, then
422 // a. If nx is +βπ½, return an implementation-approximated value representing -Ο / 4.
423 // b. If nx is -βπ½, return an implementation-approximated value representing -3Ο / 4.
424 // c. Return an implementation-approximated value representing -Ο / 2.
425 // 6. If ny is +0π½, then
426 // a. If nx > +0π½ or nx is +0π½, return +0π½.
427 // b. Return an implementation-approximated value representing Ο.
428 // 7. If ny is -0π½, then
429 // a. If nx > +0π½ or nx is +0π½, return -0π½.
430 // b. Return an implementation-approximated value representing -Ο.
431 // 8. Assert: ny is finite and is neither +0π½ nor -0π½.
432 // 9. If ny > +0π½, then
433 // a. If nx is +βπ½, return +0π½.
434 // b. If nx is -βπ½, return an implementation-approximated value representing Ο.
435 // c. If nx is +0π½ or nx is -0π½, return an implementation-approximated value representing Ο / 2.
436 // 10. If ny < +0π½, then
437 // a. If nx is +βπ½, return -0π½.
438 // b. If nx is -βπ½, return an implementation-approximated value representing -Ο.
439 // c. If nx is +0π½ or nx is -0π½, return an implementation-approximated value representing -Ο / 2.
440 // 11. Assert: nx is finite and is neither +0π½ nor -0π½.
441 // 12. Return an implementation-approximated value representing the result of the inverse tangent of the quotient β(ny) / β(nx).
442 Ok(y.atan2(x).into())
443 }
444
445 /// Get the cubic root of a number.
446 ///
447 /// More information:
448 /// - [ECMAScript reference][spec]
449 /// - [MDN documentation][mdn]
450 ///
451 /// [spec]: https://tc39.es/ecma262/#sec-math.cbrt
452 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt
453 pub(crate) fn cbrt(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
454 Ok(args
455 .get_or_undefined(0)
456 // 1. Let n be ? ToNumber(x).
457 .to_number(context)?
458 // 2. If n is NaN, n is +0π½, n is -0π½, n is +βπ½, or n is -βπ½, return n.
459 // 3. Return an implementation-approximated value representing the result of the cube root of β(n).
460 .cbrt()
461 .into())
462 }
463
464 /// Get lowest integer above a number.
465 ///
466 /// More information:
467 /// - [ECMAScript reference][spec]
468 /// - [MDN documentation][mdn]
469 ///
470 /// [spec]: https://tc39.es/ecma262/#sec-math.ceil
471 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil
472 pub(crate) fn ceil(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
473 Ok(args
474 .get_or_undefined(0)
475 // 1. Let n be ? ToNumber(x).
476 .to_number(context)?
477 // 2. If n is NaN, n is +0π½, n is -0π½, n is +βπ½, or n is -βπ½, return n.
478 // 3. If n < +0π½ and n > -1π½, return -0π½.
479 // 4. If n is an integral Number, return n.
480 // 5. Return the smallest (closest to -β) integral Number value that is not less than n.
481 .ceil()
482 .into())
483 }
484
485 /// Get the number of leading zeros in the 32 bit representation of a number
486 ///
487 /// More information:
488 /// - [ECMAScript reference][spec]
489 /// - [MDN documentation][mdn]
490 ///
491 /// [spec]: https://tc39.es/ecma262/#sec-math.clz32
492 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32
493 pub(crate) fn clz32(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
494 Ok(args
495 .get_or_undefined(0)
496 // 1. Let n be ? ToUint32(x).
497 .to_u32(context)?
498 // 2. Let p be the number of leading zero bits in the unsigned 32-bit binary representation of n.
499 // 3. Return π½(p).
500 .leading_zeros()
501 .into())
502 }
503
504 /// Get the cosine of a number.
505 ///
506 /// More information:
507 /// - [ECMAScript reference][spec]
508 /// - [MDN documentation][mdn]
509 ///
510 /// [spec]: https://tc39.es/ecma262/#sec-math.cos
511 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos
512 pub(crate) fn cos(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
513 Ok(args
514 .get_or_undefined(0)
515 // 1. Let n be ? ToNumber(x).
516 .to_number(context)?
517 // 2. If n is NaN, n is +βπ½, or n is -βπ½, return NaN.
518 // 3. If n is +0π½ or n is -0π½, return 1π½.
519 // 4. Return an implementation-approximated value representing the result of the cosine of β(n).
520 .cos()
521 .into())
522 }
523
524 /// Get the hyperbolic cosine of a number.
525 ///
526 /// More information:
527 /// - [ECMAScript reference][spec]
528 /// - [MDN documentation][mdn]
529 ///
530 /// [spec]: https://tc39.es/ecma262/#sec-math.cosh
531 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh
532 pub(crate) fn cosh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
533 Ok(args
534 .get_or_undefined(0)
535 // 1. Let n be ? ToNumber(x).
536 .to_number(context)?
537 // 2. If n is NaN, return NaN.
538 // 3. If n is +βπ½ or n is -βπ½, return +βπ½.
539 // 4. If n is +0π½ or n is -0π½, return 1π½.
540 // 5. Return an implementation-approximated value representing the result of the hyperbolic cosine of β(n).
541 .cosh()
542 .into())
543 }
544
545 /// Get the power to raise the natural logarithm to get the number.
546 ///
547 /// More information:
548 /// - [ECMAScript reference][spec]
549 /// - [MDN documentation][mdn]
550 ///
551 /// [spec]: https://tc39.es/ecma262/#sec-math.exp
552 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp
553 pub(crate) fn exp(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
554 Ok(args
555 .get_or_undefined(0)
556 // 1. Let n be ? ToNumber(x).
557 .to_number(context)?
558 // 2. If n is NaN or n is +βπ½, return n.
559 // 3. If n is +0π½ or n is -0π½, return 1π½.
560 // 4. If n is -βπ½, return +0π½.
561 // 5. Return an implementation-approximated value representing the result of the exponential function of β(n).
562 .exp()
563 .into())
564 }
565
566 /// The `Math.expm1()` function returns e^x - 1, where x is the argument, and e the base of
567 /// the natural logarithms. The result is computed in a way that is accurate even when the
568 /// value of x is close 0
569 ///
570 /// More information:
571 /// - [ECMAScript reference][spec]
572 /// - [MDN documentation][mdn]
573 ///
574 /// [spec]: https://tc39.es/ecma262/#sec-math.expm1
575 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/expm1
576 pub(crate) fn expm1(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
577 Ok(args
578 .get_or_undefined(0)
579 // 1. Let n be ? ToNumber(x).
580 .to_number(context)?
581 // 2. If n is NaN, n is +0π½, n is -0π½, or n is +βπ½, return n.
582 // 3. If n is -βπ½, return -1π½.
583 // 4. Return an implementation-approximated value representing the result of subtracting 1 from the exponential function of β(n).
584 .exp_m1()
585 .into())
586 }
587
588 /// Get the highest integer below a number.
589 ///
590 /// More information:
591 /// - [ECMAScript reference][spec]
592 /// - [MDN documentation][mdn]
593 ///
594 /// [spec]: https://tc39.es/ecma262/#sec-math.floor
595 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor
596 pub(crate) fn floor(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
597 Ok(args
598 .get_or_undefined(0)
599 // 1. Let n be ? ToNumber(x).
600 .to_number(context)?
601 // 2. If n is NaN, n is +0π½, n is -0π½, n is +βπ½, or n is -βπ½, return n.
602 // 3. If n < 1π½ and n > +0π½, return +0π½.
603 // 4. If n is an integral Number, return n.
604 // 5. Return the greatest (closest to +β) integral Number value that is not greater than n.
605 .floor()
606 .into())
607 }
608
609 /// The `Math.f16round()` static method returns the nearest 16-bit half precision
610 /// float representation of a number.
611 ///
612 /// More information:
613 /// - [ECMAScript reference][spec]
614 /// - [MDN documentation][mdn]
615 ///
616 /// [spec]: https://tc39.es/ecma262/#sec-math.round
617 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/f16round
618 #[allow(clippy::float_cmp)]
619 #[cfg(feature = "float16")]
620 pub(crate) fn f16round(
621 _: &JsValue,
622 args: &[JsValue],
623 context: &mut Context,
624 ) -> JsResult<JsValue> {
625 let num = args
626 .get_or_undefined(0)
627 //1. Let n be ? ToNumber(x).
628 .to_number(context)?;
629
630 // 2. If n is NaN, return NaN.
631 // 3. If n is one of +0π½, -0π½, +βπ½, or -βπ½, return n.
632 // 4. Let n16 be the result of converting n to IEEE 754-2019 binary16 format using roundTiesToEven mode.
633 // 5. Let n64 be the result of converting n16 to IEEE 754-2019 binary64 format.
634 // 6. Return the ECMAScript Number value corresponding to n64.
635 Ok(float16::f16::from_f64(num).to_f64().into())
636 }
637
638 /// Get the nearest 32-bit single precision float representation of a number.
639 ///
640 /// More information:
641 /// - [ECMAScript reference][spec]
642 /// - [MDN documentation][mdn]
643 ///
644 /// [spec]: https://tc39.es/ecma262/#sec-math.fround
645 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround
646 pub(crate) fn fround(
647 _: &JsValue,
648 args: &[JsValue],
649 context: &mut Context,
650 ) -> JsResult<JsValue> {
651 // 1. Let n be ? ToNumber(x).
652 let x = args.get_or_undefined(0).to_number(context)?;
653
654 // 2. If n is NaN, return NaN.
655 // 3. If n is one of +0π½, -0π½, +βπ½, or -βπ½, return n.
656 // 4. Let n32 be the result of converting n to a value in IEEE 754-2019 binary32 format using roundTiesToEven mode.
657 // 5. Let n64 be the result of converting n32 to a value in IEEE 754-2019 binary64 format.
658 // 6. Return the ECMAScript Number value corresponding to n64.
659 Ok(f64::from(x as f32).into())
660 }
661
662 /// Get an approximation of the square root of the sum of squares of all arguments.
663 ///
664 /// More information:
665 /// - [ECMAScript reference][spec]
666 /// - [MDN documentation][mdn]
667 ///
668 /// [spec]: https://tc39.es/ecma262/#sec-math.hypot
669 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot
670 pub(crate) fn hypot(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
671 // 1. Let coerced be a new empty List.
672 // 2. For each element arg of args, do
673 // a. Let n be ? ToNumber(arg).
674 // b. Append n to coerced.
675 // 3. For each element number of coerced, do
676 // 5. For each element number of coerced, do
677 let mut small = [0f64; 8];
678 let mut large = Vec::new();
679 let coerced: &mut [f64] = if args.len() <= small.len() {
680 &mut small[..args.len()]
681 } else {
682 large.resize(args.len(), 0f64);
683 &mut large
684 };
685 for (slot, arg) in coerced.iter_mut().zip(args) {
686 *slot = arg.to_number(context)?;
687 }
688 // a. If number is +βπ½ or number is -βπ½, return +βπ½.
689 if coerced.iter().any(|n| n.is_infinite()) {
690 return Ok(f64::INFINITY.into());
691 }
692 // a. If number is NaN, return NaN.
693 if coerced.iter().any(|n| n.is_nan()) {
694 return Ok(f64::NAN.into());
695 }
696 // 4. Let onlyZero be true.
697 // b. If number is neither +0π½ nor -0π½, set onlyZero to false.
698 // 6. If onlyZero is true, return +0π½.
699 let max = coerced.iter().fold(0f64, |max, n| max.max(n.abs()));
700 if max == 0.0 {
701 return Ok(0.into());
702 }
703 // 7. Return an implementation-approximated value representing the square root of the sum of squares of the mathematical values of the elements of coerced.
704 // Scaled by the largest magnitude and Kahan-summed, as V8 does, so results match it bit for bit.
705 let (mut sum, mut compensation) = (0f64, 0f64);
706 for n in coerced.iter() {
707 let scaled = n / max;
708 let summand = scaled * scaled - compensation;
709 let preliminary = sum + summand;
710 compensation = (preliminary - sum) - summand;
711 sum = preliminary;
712 }
713
714 Ok((sum.sqrt() * max).into())
715 }
716
717 /// Get the result of the C-like 32-bit multiplication of the two parameters.
718 ///
719 /// More information:
720 /// - [ECMAScript reference][spec]
721 /// - [MDN documentation][mdn]
722 ///
723 /// [spec]: https://tc39.es/ecma262/#sec-math.imul
724 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
725 pub(crate) fn imul(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
726 // 1. Let a be β(? ToUint32(x)).
727 let x = args.get_or_undefined(0).to_u32(context)?;
728
729 // 2. Let b be β(? ToUint32(y)).
730 let y = args.get_or_undefined(1).to_u32(context)?;
731
732 // 3. Let product be (a Γ b) modulo 2^32.
733 // 4. If product β₯ 2^31, return π½(product - 2^32); otherwise return π½(product).
734 Ok((x.wrapping_mul(y) as i32).into())
735 }
736
737 /// Get the natural logarithm of a number.
738 ///
739 /// More information:
740 /// - [ECMAScript reference][spec]
741 /// - [MDN documentation][mdn]
742 ///
743 /// [spec]: https://tc39.es/ecma262/#sec-math.log
744 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log
745 pub(crate) fn log(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
746 Ok(args
747 .get_or_undefined(0)
748 // 1. Let n be ? ToNumber(x).
749 .to_number(context)?
750 // 2. If n is NaN or n is +βπ½, return n.
751 // 3. If n is 1π½, return +0π½.
752 // 4. If n is +0π½ or n is -0π½, return -βπ½.
753 // 5. If n < +0π½, return NaN.
754 // 6. Return an implementation-approximated value representing the result of the natural logarithm of β(n).
755 .ln()
756 .into())
757 }
758
759 /// Get approximation to the natural logarithm of 1 + x.
760 ///
761 /// More information:
762 /// - [ECMAScript reference][spec]
763 /// - [MDN documentation][mdn]
764 ///
765 /// [spec]: https://tc39.es/ecma262/#sec-math.log1p
766 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log1p
767 pub(crate) fn log1p(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
768 Ok(args
769 .get_or_undefined(0)
770 // 1. Let n be ? ToNumber(x).
771 .to_number(context)?
772 // 2. If n is NaN, n is +0π½, n is -0π½, or n is +βπ½, return n.
773 // 3. If n is -1π½, return -βπ½.
774 // 4. If n < -1π½, return NaN.
775 // 5. Return an implementation-approximated value representing the result of the natural logarithm of 1 + β(n).
776 .ln_1p()
777 .into())
778 }
779
780 /// Get the base 10 logarithm of the number.
781 ///
782 /// More information:
783 /// - [ECMAScript reference][spec]
784 /// - [MDN documentation][mdn]
785 ///
786 /// [spec]: https://tc39.es/ecma262/#sec-math.log10
787 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10
788 pub(crate) fn log10(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
789 Ok(args
790 .get_or_undefined(0)
791 // 1. Let n be ? ToNumber(x).
792 .to_number(context)?
793 // 2. If n is NaN or n is +βπ½, return n.
794 // 3. If n is 1π½, return +0π½.
795 // 4. If n is +0π½ or n is -0π½, return -βπ½.
796 // 5. If n < +0π½, return NaN.
797 // 6. Return an implementation-approximated value representing the result of the base 10 logarithm of β(n).
798 .log10()
799 .into())
800 }
801
802 /// Get the base 2 logarithm of the number.
803 ///
804 /// More information:
805 /// - [ECMAScript reference][spec]
806 /// - [MDN documentation][mdn]
807 ///
808 /// [spec]: https://tc39.es/ecma262/#sec-math.log2
809 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2
810 pub(crate) fn log2(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
811 Ok(args
812 .get_or_undefined(0)
813 // 1. Let n be ? ToNumber(x).
814 .to_number(context)?
815 // 2. If n is NaN or n is +βπ½, return n.
816 // 3. If n is 1π½, return +0π½.
817 // 4. If n is +0π½ or n is -0π½, return -βπ½.
818 // 5. If n < +0π½, return NaN.
819 // 6. Return an implementation-approximated value representing the result of the base 2 logarithm of β(n).
820 .log2()
821 .into())
822 }
823
824 /// Get the maximum of several numbers.
825 ///
826 /// More information:
827 /// - [ECMAScript reference][spec]
828 /// - [MDN documentation][mdn]
829 ///
830 /// [spec]: https://tc39.es/ecma262/#sec-math.max
831 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
832 pub(crate) fn max(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
833 // 1. Let coerced be a new empty List.
834 // 2. For each element arg of args, do
835 // b. Append n to coerced.
836 // 3. Let highest be -βπ½.
837 let mut highest = f64::NEG_INFINITY;
838
839 // 4. For each element number of coerced, do
840 for arg in args {
841 // a. Let n be ? ToNumber(arg).
842 let num = arg.to_number(context)?;
843 // a. If number is NaN, return NaN. b. +0 is above -0. c. Otherwise the maximum.
844 highest = Self::max_step(highest, num);
845 }
846 // 5. Return highest.
847 Ok(highest.into())
848 }
849
850 /// Get the minimum of several numbers.
851 ///
852 /// More information:
853 /// - [ECMAScript reference][spec]
854 /// - [MDN documentation][mdn]
855 ///
856 /// [spec]: https://tc39.es/ecma262/#sec-math.min
857 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min
858 pub(crate) fn min(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
859 // 1. Let coerced be a new empty List.
860 // 2. For each element arg of args, do
861 // b. Append n to coerced.
862 // 3. Let lowest be +βπ½.
863 let mut lowest = f64::INFINITY;
864
865 // 4. For each element number of coerced, do
866 for arg in args {
867 // a. Let n be ? ToNumber(arg).
868 let num = arg.to_number(context)?;
869 // a. If number is NaN, return NaN. b. -0 is below +0. c. Otherwise the minimum.
870 lowest = Self::min_step(lowest, num);
871 }
872 // 5. Return lowest.
873 Ok(lowest.into())
874 }
875
876 /// Raise a number to a power.
877 ///
878 /// More information:
879 /// - [ECMAScript reference][spec]
880 /// - [MDN documentation][mdn]
881 ///
882 /// [spec]: https://tc39.es/ecma262/#sec-math.pow
883 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow
884 #[allow(clippy::float_cmp)]
885 pub(crate) fn pow(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
886 // 1. Set base to ? ToNumber(base).
887 let x = args.get_or_undefined(0).to_number(context)?;
888
889 // 2. Set exponent to ? ToNumber(exponent).
890 let y = args.get_or_undefined(1).to_number(context)?;
891
892 // 3. Return Number::exponentiate(base, exponent).
893 Ok(Self::exponentiate(x, y).into())
894 }
895
896 /// `Number::exponentiate ( base, exponent )`, shared by `Math.pow` and the `**` operator.
897 #[allow(clippy::float_cmp)]
898 pub(crate) fn exponentiate(x: f64, y: f64) -> f64 {
899 // https://github.com/rust-lang/rust/issues/60468
900
901 // https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-exponentiate
902 // 6.1.6.1.3 Number::exponentiate ( base, exponent )
903 // 1. If exponent is NaN, return NaN.
904 if y.is_nan() {
905 return f64::NAN;
906 }
907
908 // 9. If exponent is +βπ½, then
909 // a. If abs(β(base)) > 1, return +βπ½.
910 // b. If abs(β(base)) = 1, return NaN.
911 // c. If abs(β(base)) < 1, return +0π½.
912 // 10. If exponent is -βπ½, then
913 // a. If abs(β(base)) > 1, return +0π½.
914 // b. If abs(β(base)) = 1, return NaN.
915 // c. If abs(β(base)) < 1, return +βπ½.
916 if f64::abs(x) == 1f64 && y.is_infinite() {
917 return f64::NAN;
918 }
919
920 // fdlibm's pow, which browsers follow, returns `x * x` for an exponent of 2 and the square
921 // root of a non-negative base (sign bit clear) for an exponent of 0.5.
922 if y == 2.0 {
923 return x * x;
924 }
925 if y == 0.5 && x.is_sign_positive() {
926 return x.sqrt();
927 }
928
929 x.powf(y)
930 }
931
932 /// Generate a random floating-point number between `0` and `1`.
933 ///
934 /// More information:
935 /// - [ECMAScript reference][spec]
936 /// - [MDN documentation][mdn]
937 ///
938 /// [spec]: https://tc39.es/ecma262/#sec-math.random
939 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
940 #[allow(clippy::unnecessary_wraps)]
941 pub(crate) fn random(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
942 // NOTE: Each Math.random function created for distinct realms must produce a distinct sequence of values from successive calls.
943 Ok(rand::random::<f64>().into())
944 }
945
946 /// Round a number to the nearest integer.
947 ///
948 /// More information:
949 /// - [ECMAScript reference][spec]
950 /// - [MDN documentation][mdn]
951 ///
952 /// [spec]: https://tc39.es/ecma262/#sec-math.round
953 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
954 #[allow(clippy::float_cmp)]
955 pub(crate) fn round(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
956 let num = args
957 .get_or_undefined(0)
958 //1. Let n be ? ToNumber(x).
959 .to_number(context)?;
960
961 //2. If n is NaN, +βπ½, -βπ½, or an integral Number, return n.
962 //3. If n < 0.5π½ and n > +0π½, return +0π½.
963 //4. If n < +0π½ and n β₯ -0.5π½, return -0π½.
964 //5. Return the integral Number closest to n, preferring the Number closer to +β in the case of a tie.
965
966 if num.fract() == -0.5 {
967 Ok(num.ceil().into())
968 } else {
969 Ok(num.round().into())
970 }
971 }
972
973 /// Get the sign of a number.
974 ///
975 /// More information:
976 /// - [ECMAScript reference][spec]
977 /// - [MDN documentation][mdn]
978 ///
979 /// [spec]: https://tc39.es/ecma262/#sec-math.sign
980 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
981 pub(crate) fn sign(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
982 // 1. Let n be ? ToNumber(x).
983 let n = args.get_or_undefined(0).to_number(context)?;
984
985 // 2. If n is NaN, n is +0π½, or n is -0π½, return n.
986 if n == 0f64 {
987 return Ok(n.into());
988 }
989 // 3. If n < +0π½, return -1π½.
990 // 4. Return 1π½.
991 Ok(n.signum().into())
992 }
993
994 /// Get the sine of a number.
995 ///
996 /// More information:
997 /// - [ECMAScript reference][spec]
998 /// - [MDN documentation][mdn]
999 ///
1000 /// [spec]: https://tc39.es/ecma262/#sec-math.sin
1001 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin
1002 pub(crate) fn sin(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1003 Ok(args
1004 .get_or_undefined(0)
1005 // 1. Let n be ? ToNumber(x).
1006 .to_number(context)?
1007 // 2. If n is NaN, n is +0π½, or n is -0π½, return n.
1008 // 3. If n is +βπ½ or n is -βπ½, return NaN.
1009 // 4. Return an implementation-approximated value representing the result of the sine of β(n).
1010 .sin()
1011 .into())
1012 }
1013
1014 /// Get the hyperbolic sine of a number.
1015 ///
1016 /// More information:
1017 /// - [ECMAScript reference][spec]
1018 /// - [MDN documentation][mdn]
1019 ///
1020 /// [spec]: https://tc39.es/ecma262/#sec-math.sinh
1021 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh
1022 pub(crate) fn sinh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1023 Ok(args
1024 .get_or_undefined(0)
1025 // 1. Let n be ? ToNumber(x).
1026 .to_number(context)?
1027 // 2. If n is NaN, n is +0π½, n is -0π½, n is +βπ½, or n is -βπ½, return n.
1028 // 3. Return an implementation-approximated value representing the result of the hyperbolic sine of β(n).
1029 .sinh()
1030 .into())
1031 }
1032
1033 /// Get the square root of a number.
1034 ///
1035 /// More information:
1036 /// - [ECMAScript reference][spec]
1037 /// - [MDN documentation][mdn]
1038 ///
1039 /// [spec]: https://tc39.es/ecma262/#sec-math.sqrt
1040 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt
1041 pub(crate) fn sqrt(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1042 Ok(args
1043 .get_or_undefined(0)
1044 // 1. Let n be ? ToNumber(x).
1045 .to_number(context)?
1046 // 2. If n is NaN, n is +0π½, n is -0π½, or n is +βπ½, return n.
1047 // 3. If n < +0π½, return NaN.
1048 // 4. Return an implementation-approximated value representing the result of the square root of β(n).
1049 .sqrt()
1050 .into())
1051 }
1052
1053 /// Get the tangent of a number.
1054 ///
1055 /// More information:
1056 /// - [ECMAScript reference][spec]
1057 /// - [MDN documentation][mdn]
1058 ///
1059 /// [spec]: https://tc39.es/ecma262/#sec-math.tan
1060 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan
1061 pub(crate) fn tan(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1062 Ok(args
1063 .get_or_undefined(0)
1064 // 1. Let n be ? ToNumber(x).
1065 .to_number(context)?
1066 // 2. If n is NaN, n is +0π½, or n is -0π½, return n.
1067 // 3. If n is +βπ½, or n is -βπ½, return NaN.
1068 // 4. Return an implementation-approximated value representing the result of the tangent of β(n).
1069 .tan()
1070 .into())
1071 }
1072
1073 /// Get the hyperbolic tangent of a number.
1074 ///
1075 /// More information:
1076 /// - [ECMAScript reference][spec]
1077 /// - [MDN documentation][mdn]
1078 ///
1079 /// [spec]: https://tc39.es/ecma262/#sec-math.tanh
1080 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh
1081 pub(crate) fn tanh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1082 Ok(args
1083 .get_or_undefined(0)
1084 // 1. Let n be ? ToNumber(x).
1085 .to_number(context)?
1086 // 2. If n is NaN, n is +0π½, or n is -0π½, return n.
1087 // 3. If n is +βπ½, return 1π½.
1088 // 4. If n is -βπ½, return -1π½.
1089 // 5. Return an implementation-approximated value representing the result of the hyperbolic tangent of β(n).
1090 .tanh()
1091 .into())
1092 }
1093
1094 /// Get the integer part of a number.
1095 ///
1096 /// More information:
1097 /// - [ECMAScript reference][spec]
1098 /// - [MDN documentation][mdn]
1099 ///
1100 /// [spec]: https://tc39.es/ecma262/#sec-math.trunc
1101 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc
1102 pub(crate) fn trunc(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
1103 Ok(args
1104 .get_or_undefined(0)
1105 // 1. Let n be ? ToNumber(x).
1106 .to_number(context)?
1107 // 2. If n is NaN, n is +0π½, n is -0π½, n is +βπ½, or n is -βπ½, return n.
1108 // 3. If n < 1π½ and n > +0π½, return +0π½.
1109 // 4. If n < +0π½ and n > -1π½, return -0π½.
1110 // 5. Return the integral Number nearest n in the direction of +0π½.
1111 .trunc()
1112 .into())
1113 }
1114
1115 /// Sum each value in an iterable
1116 ///
1117 /// More information:
1118 /// - [ECMAScript reference][spec]
1119 /// - [MDN documentation][mdn]
1120 ///
1121 /// [spec]: https://tc39.es/ecma262/#sec-math.sumprecise
1122 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sumPrecise
1123 #[cfg(feature = "xsum")]
1124 pub(crate) fn sum_precise(
1125 _: &JsValue,
1126 args: &[JsValue],
1127 context: &mut Context,
1128 ) -> JsResult<JsValue> {
1129 const ITERATION_MAX: u64 = 2u64.pow(53) - 1;
1130 use xsum::{Xsum, XsumLarge, XsumSmall, XsumVariant, constants::XSUM_THRESHOLD};
1131
1132 use crate::{builtins::iterable::IteratorHint, js_error};
1133
1134 let items = args.get_or_undefined(0);
1135 // NOTE (nekevss): inline `RequireObjectCoercible`
1136 // 1. Perform ? RequireObjectCoercible(items).
1137 // RequireObjectCoercible, 1. If argument is either undefined or null, throw a TypeError exception.
1138 // RequireObjectCoercible, 2. Return argument.
1139 if items.is_null_or_undefined() {
1140 return Err(js_error!(TypeError: "value must be object coercible."));
1141 }
1142
1143 // 2. Let iteratorRecord be ? GetIterator(items, sync).
1144 let mut iterator_record = items.get_iterator(IteratorHint::Sync, context)?;
1145 // 3. Let state be minus-zero.
1146 // 4. Let sum be 0.
1147 let length = if iterator_record.iterator().is_array() {
1148 iterator_record
1149 .iterator()
1150 .length_of_array_like(context)
1151 .ok()
1152 } else {
1153 None
1154 };
1155 let mut sum = if let Some(length) = length {
1156 if length <= XSUM_THRESHOLD as u64 {
1157 XsumVariant::Small(XsumSmall::new())
1158 } else {
1159 XsumVariant::Large(XsumLarge::new())
1160 }
1161 } else {
1162 XsumVariant::Small(XsumSmall::new())
1163 };
1164 // 5. Let count be 0.
1165 let mut count = 0;
1166 // 6. Let next be not-started.
1167 // 7. Repeat, while next is not done,
1168 // a. Set next to ? IteratorStepValue(iteratorRecord).
1169 while let Some(next) = iterator_record.step_value(context)? {
1170 // b. If next is not done, then
1171 // i. If count β₯ 2**53 - 1, then
1172 if count >= ITERATION_MAX {
1173 // 1. NOTE: This step is not expected to be reached in practice and is
1174 // included only so that implementations may rely on inputs being
1175 // "reasonably sized" without violating this specification.
1176 // 2. Let error be ThrowCompletion(a newly created RangeError object).
1177 let err = Err(js_error!(RangeError: "Summation exceeded maximum iterations"));
1178 // 3. Return ? IteratorClose(iteratorRecord, error).
1179 return iterator_record.close(err, context);
1180 }
1181 // ii. If next is not a Number, then
1182 let Some(number) = next.as_number() else {
1183 // 1. Let error be ThrowCompletion(a newly created TypeError object).
1184 let err = Err(js_error!(TypeError: "sumPrecise can only be called on a number."));
1185 // 2. Return ? IteratorClose(iteratorRecord, error).
1186 return iterator_record.close(err, context);
1187 };
1188 // iii. Let n be next.
1189 // iv. If state is not not-a-number, then
1190 // 1. If n is NaN, then
1191 // a. Set state to not-a-number.
1192 // 2. Else if n is +βπ½, then
1193 // a. If state is minus-infinity, set state to not-a-number.
1194 // b. Else, set state to plus-infinity.
1195 // 3. Else if n is -βπ½, then
1196 // a. If state is plus-infinity, set state to not-a-number.
1197 // b. Else, set state to minus-infinity.
1198 // 4. Else if n is not -0π½ and state is either minus-zero or finite, then
1199 // a. Set state to finite.
1200 // b. Set sum to sum + β(n).
1201 sum.add(number);
1202 // v. Set count to count + 1.
1203 count += 1;
1204 }
1205 // 8. If state is not-a-number, return NaN.
1206 // 9. If state is plus-infinity, return +βπ½.
1207 // 10. If state is minus-infinity, return -βπ½.
1208 // 11. If state is minus-zero, return -0π½.
1209 // 12. Return π½(sum).
1210 Ok(sum.sum().into())
1211 }
1212}