diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index 152797f7c2..cd171d6740 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -165,6 +165,21 @@ impl Math { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).ceil()).into()) } + /// Get the number of leading zeros in the 32 bit representation of a number + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-math.clz32 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 + pub(crate) fn clz32(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + Ok(args + .get(0) + .map_or(32i32, |x| (f64::from(x) as u32).leading_zeros() as i32) + .into()) + } + /// Get the cosine of a number. /// /// More information: @@ -201,6 +216,23 @@ impl Math { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).exp()).into()) } + /// The Math.expm1() function returns e^x - 1, where x is the argument, and e the base of + /// the natural logarithms. The result is computed in a way that is accurate even when the + /// value of x is close 0 + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-math.expm1 + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/expm1 + pub(crate) fn expm1(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + Ok(args + .get(0) + .map_or(f64::NAN, |x| f64::from(x).exp_m1()) + .into()) + } + /// Get the highest integer below a number. /// /// More information: @@ -216,6 +248,47 @@ impl Math { .into()) } + /// Get the nearest 32-bit single precision float representation of a number. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-math.fround + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround + pub(crate) fn fround(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + Ok(args + .get(0) + .map_or(f64::NAN, |x| (f64::from(x) as f32) as f64) + .into()) + } + + /// Get an approximation of the square root of the sum of squares of all arguments. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-math.hypot + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot + pub(crate) fn hypot(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + Ok(args.iter().fold(0f64, |x, v| f64::from(v).hypot(x)).into()) + } + + /// Get the result of the C-like 32-bit multiplication of the two parameters. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-math.imul + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul + pub(crate) fn imul(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + let a = args.get(0).map_or(0f64, f64::from); + let b = args.get(1).map_or(0f64, f64::from); + Ok(((a as u32).wrapping_mul(b as u32) as i32).into()) + } + /// Get the natural logarithm of a number. /// /// More information: @@ -238,6 +311,21 @@ impl Math { .into()) } + /// Get approximation to the natural logarithm of 1 + x. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-math.log1p + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log1p + pub(crate) fn log1p(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + Ok(args + .get(0) + .map_or(f64::NAN, |x| f64::from(x).ln_1p()) + .into()) + } + /// Get the base 10 logarithm of the number. /// /// More information: @@ -485,11 +573,17 @@ impl Math { make_builtin_fn(Self::atan2, "atan2", &math, 2); make_builtin_fn(Self::cbrt, "cbrt", &math, 1); make_builtin_fn(Self::ceil, "ceil", &math, 1); + make_builtin_fn(Self::clz32, "clz32", &math, 1); make_builtin_fn(Self::cos, "cos", &math, 1); make_builtin_fn(Self::cosh, "cosh", &math, 1); make_builtin_fn(Self::exp, "exp", &math, 1); + make_builtin_fn(Self::expm1, "expm1", &math, 1); make_builtin_fn(Self::floor, "floor", &math, 1); + make_builtin_fn(Self::fround, "fround", &math, 1); + make_builtin_fn(Self::hypot, "hypot", &math, 1); + make_builtin_fn(Self::imul, "imul", &math, 1); make_builtin_fn(Self::log, "log", &math, 1); + make_builtin_fn(Self::log1p, "log1p", &math, 1); make_builtin_fn(Self::log10, "log10", &math, 1); make_builtin_fn(Self::log2, "log2", &math, 1); make_builtin_fn(Self::max, "max", &math, 2); diff --git a/boa/src/builtins/math/tests.rs b/boa/src/builtins/math/tests.rs index a733946d69..99f4387889 100644 --- a/boa/src/builtins/math/tests.rs +++ b/boa/src/builtins/math/tests.rs @@ -183,6 +183,42 @@ fn ceil() { assert_eq!(c.to_number(), -7_f64); } +#[test] +fn clz32() { + let realm = Realm::create(); + let mut engine = Interpreter::new(realm); + let init = r#" + var a = Math.clz32(); + var b = Math.clz32({}); + var c = Math.clz32(-173); + var d = Math.clz32("1"); + var e = Math.clz32(2147483647); + var f = Math.clz32(Infinity); + var g = Math.clz32(true); + var h = Math.clz32(0); + "#; + + eprintln!("{}", forward(&mut engine, init)); + + let a = forward_val(&mut engine, "a").unwrap(); + let b = forward_val(&mut engine, "b").unwrap(); + let c = forward_val(&mut engine, "c").unwrap(); + let d = forward_val(&mut engine, "d").unwrap(); + let e = forward_val(&mut engine, "e").unwrap(); + let f = forward_val(&mut engine, "f").unwrap(); + let g = forward_val(&mut engine, "g").unwrap(); + let h = forward_val(&mut engine, "h").unwrap(); + + assert_eq!(a.to_number(), 32_f64); + assert_eq!(b.to_number(), 32_f64); + assert_eq!(c.to_number(), 0_f64); + assert_eq!(d.to_number(), 31_f64); + assert_eq!(e.to_number(), 1_f64); + assert_eq!(f.to_number(), 32_f64); + assert_eq!(g.to_number(), 31_f64); + assert_eq!(h.to_number(), 32_f64); +} + #[test] fn cos() { let realm = Realm::create(); @@ -243,6 +279,36 @@ fn exp() { assert_eq!(c.to_number(), 7.389_056_098_930_65); } +#[test] +fn expm1() { + let realm = Realm::create(); + let mut engine = Interpreter::new(realm); + let init = r#" + var a = Math.expm1(); + var b = Math.expm1({}); + var c = Math.expm1(1); + var d = Math.expm1(-1); + var e = Math.expm1(0); + var f = Math.expm1(2); + "#; + + eprintln!("{}", forward(&mut engine, init)); + + let a = forward(&mut engine, "a"); + let b = forward(&mut engine, "b"); + let c = forward_val(&mut engine, "c").unwrap(); + let d = forward_val(&mut engine, "d").unwrap(); + let e = forward_val(&mut engine, "e").unwrap(); + let f = forward_val(&mut engine, "f").unwrap(); + + assert_eq!(a, String::from("NaN")); + assert_eq!(b, String::from("NaN")); + assert_eq!(c.to_number(), 1.718_281_828_459_045); + assert_eq!(d.to_number(), -0.632_120_558_828_557_7); + assert_eq!(e.to_number(), 0_f64); + assert_eq!(f.to_number(), 6.389_056_098_930_65); +} + #[test] fn floor() { let realm = Realm::create(); @@ -264,6 +330,102 @@ fn floor() { assert_eq!(c.to_number(), 3_f64); } +#[test] +fn fround() { + let realm = Realm::create(); + let mut engine = Interpreter::new(realm); + let init = r#" + var a = Math.fround(NaN); + var b = Math.fround(Infinity); + var c = Math.fround(5); + var d = Math.fround(5.5); + var e = Math.fround(5.05); + var f = Math.fround(-5.05); + var g = Math.fround(); + "#; + + eprintln!("{}", forward(&mut engine, init)); + + let a = forward(&mut engine, "a"); + let b = forward(&mut engine, "b"); + let c = forward_val(&mut engine, "c").unwrap(); + let d = forward_val(&mut engine, "d").unwrap(); + let e = forward_val(&mut engine, "e").unwrap(); + let f = forward_val(&mut engine, "f").unwrap(); + let g = forward(&mut engine, "g"); + + assert_eq!(a, String::from("NaN")); + assert_eq!(b, String::from("Infinity")); + assert_eq!(c.to_number(), 5f64); + assert_eq!(d.to_number(), 5.5f64); + assert_eq!(e.to_number(), 5.050_000_190_734_863); + assert_eq!(f.to_number(), -5.050_000_190_734_863); + assert_eq!(g, String::from("NaN")); +} + +#[test] +fn hypot() { + let realm = Realm::create(); + let mut engine = Interpreter::new(realm); + let init = r#" + var a = Math.hypot(); + var b = Math.hypot(3, 4); + var c = Math.hypot(5, 12); + var d = Math.hypot(3, 4, -5); + var e = Math.hypot(4, [5], 6); + var f = Math.hypot(3, -Infinity); + var g = Math.hypot(12); + "#; + + eprintln!("{}", forward(&mut engine, init)); + + let a = forward_val(&mut engine, "a").unwrap(); + let b = forward_val(&mut engine, "b").unwrap(); + let c = forward_val(&mut engine, "c").unwrap(); + let d = forward_val(&mut engine, "d").unwrap(); + let e = forward(&mut engine, "e"); + let f = forward(&mut engine, "f"); + let g = forward_val(&mut engine, "g").unwrap(); + + assert_eq!(a.to_number(), 0f64); + assert_eq!(b.to_number(), 5f64); + assert_eq!(c.to_number(), 13f64); + assert_eq!(d.to_number(), 7.071_067_811_865_475_5); + assert_eq!(e, String::from("NaN")); + assert_eq!(f, String::from("Infinity")); + assert_eq!(g.to_number(), 12f64); +} + +#[test] +fn imul() { + let realm = Realm::create(); + let mut engine = Interpreter::new(realm); + let init = r#" + var a = Math.imul(3, 4); + var b = Math.imul(-5, 12); + var c = Math.imul(0xffffffff, 5); + var d = Math.imul(0xfffffffe, 5); + var e = Math.imul(12); + var f = Math.imul(); + "#; + + eprintln!("{}", forward(&mut engine, init)); + + let a = forward_val(&mut engine, "a").unwrap(); + let b = forward_val(&mut engine, "b").unwrap(); + let c = forward_val(&mut engine, "c").unwrap(); + let d = forward_val(&mut engine, "d").unwrap(); + let e = forward_val(&mut engine, "e").unwrap(); + let f = forward_val(&mut engine, "f").unwrap(); + + assert_eq!(a.to_number(), 12f64); + assert_eq!(b.to_number(), -60f64); + assert_eq!(c.to_number(), -5f64); + assert_eq!(d.to_number(), -10f64); + assert_eq!(e.to_number(), 0f64); + assert_eq!(f.to_number(), 0f64); +} + #[test] fn log() { let realm = Realm::create(); @@ -285,6 +447,39 @@ fn log() { assert_eq!(c, String::from("NaN")); } +#[test] +fn log1p() { + let realm = Realm::create(); + let mut engine = Interpreter::new(realm); + let init = r#" + var a = Math.log1p(1); + var b = Math.log1p(0); + var c = Math.log1p(-0.9999999999999999); + var d = Math.log1p(-1); + var e = Math.log1p(-1.000000000000001); + var f = Math.log1p(-2); + var g = Math.log1p(); + "#; + + eprintln!("{}", forward(&mut engine, init)); + + let a = forward_val(&mut engine, "a").unwrap(); + let b = forward_val(&mut engine, "b").unwrap(); + let c = forward_val(&mut engine, "c").unwrap(); + let d = forward(&mut engine, "d"); + let e = forward(&mut engine, "e"); + let f = forward(&mut engine, "f"); + let g = forward(&mut engine, "g"); + + assert_eq!(a.to_number(), f64::consts::LN_2); + assert_eq!(b.to_number(), 0f64); + assert_eq!(c.to_number(), -36.736_800_569_677_1); + assert_eq!(d, "-Infinity"); + assert_eq!(e, String::from("NaN")); + assert_eq!(f, String::from("NaN")); + assert_eq!(g, String::from("NaN")); +} + #[test] fn log10() { let realm = Realm::create(); @@ -334,7 +529,7 @@ fn max() { let init = r#" var a = Math.max(10, 20); var b = Math.max(-10, -20); - var c = Math.max(-10, 20); + var c = Math.max(-10, 20); "#; eprintln!("{}", forward(&mut engine, init)); @@ -355,7 +550,7 @@ fn min() { let init = r#" var a = Math.min(10, 20); var b = Math.min(-10, -20); - var c = Math.min(-10, 20); + var c = Math.min(-10, 20); "#; eprintln!("{}", forward(&mut engine, init)); @@ -418,7 +613,7 @@ fn sign() { let init = r#" var a = Math.sign(3); var b = Math.sign(-3); - var c = Math.sign(0); + var c = Math.sign(0); "#; eprintln!("{}", forward(&mut engine, init));