diff --git a/Cargo.lock b/Cargo.lock index 20aefb6a5af..1be1d709729 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3262,8 +3262,7 @@ checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" [[package]] name = "ryu-js" version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd29631678d6fb0903b69223673e122c32e9ae559d0960a38d574695ebc0ea15" +source = "git+https://github.com/boa-dev/ryu-js.git?rev=14cfde2f061ddd6e0eeb76d9324812de1b8c7789#14cfde2f061ddd6e0eeb76d9324812de1b8c7789" [[package]] name = "same-file" diff --git a/Cargo.toml b/Cargo.toml index fada2921a96..344d9c50517 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ members = [ exclude = [ "tests/fuzz", # Does weird things on Windows tests - "tests/src", # Just a hack to have fuzz inside tests + "tests/src", # Just a hack to have fuzz inside tests ] [workspace.package] @@ -70,7 +70,14 @@ serde = "1.0.217" static_assertions = "1.1.0" textwrap = "0.16.0" thin-vec = "0.2.13" -time = { version = "0.3.37", default-features = false, features = ["local-offset", "large-dates", "wasm-bindgen", "parsing", "formatting", "macros"] } +time = { version = "0.3.37", default-features = false, features = [ + "local-offset", + "large-dates", + "wasm-bindgen", + "parsing", + "formatting", + "macros", +] } tinystr = "0.7.5" log = "0.4.25" simple_logger = "5.0.0" @@ -98,7 +105,7 @@ measureme = "12.0.1" paste = "1.0" rand = "0.9.0" num-integer = "0.1.46" -ryu-js = "1.0.2" +ryu-js = { git = "https://github.com/boa-dev/ryu-js.git", rev = "14cfde2f061ddd6e0eeb76d9324812de1b8c7789" } tap = "1.0.1" thiserror = { version = "2.0.11", default-features = false } dashmap = "6.1.0" @@ -111,7 +118,9 @@ intrusive-collections = "0.9.7" cfg-if = "1.0.0" either = "1.13.0" sys-locale = "0.3.2" -temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "7484b2584efc7430f5ca34cf3a91e858286a3a29", default-features = false, features = ["tzdb"] } +temporal_rs = { git = "https://github.com/boa-dev/temporal.git", rev = "7484b2584efc7430f5ca34cf3a91e858286a3a29", default-features = false, features = [ + "tzdb", +] } web-time = "1.1.0" criterion = "0.5.1" float-cmp = "0.10.0" diff --git a/core/engine/src/builtins/number/mod.rs b/core/engine/src/builtins/number/mod.rs index d8e35761162..dbe2074ee93 100644 --- a/core/engine/src/builtins/number/mod.rs +++ b/core/engine/src/builtins/number/mod.rs @@ -241,7 +241,9 @@ impl Number { Some(IntegerOrInfinity::Integer(precision)) if (0..=100).contains(&precision) => // 5. If f < 0 or f > 100, throw a RangeError exception. { - f64_to_exponential_with_precision(this_num, precision as usize) + ryu_js::Buffer::new() + .format_to_exponential(this_num, precision as u8) + .into() } _ => { return Err(JsNativeError::range() @@ -922,16 +924,3 @@ fn f64_to_exponential(n: f64) -> JsString { _ => js_string!(s), } } - -/// Helper function that formats a float as a ES6-style exponential number string with a given precision. -// We can't use the same approach as in `f64_to_exponential` -// because in cases like (0.999).toExponential(0) the result will be 1e0. -// Instead we get the index of 'e', and if the next character is not '-' we insert the plus sign -fn f64_to_exponential_with_precision(n: f64, prec: usize) -> JsString { - let mut res = format!("{n:.prec$e}"); - let idx = res.find('e').expect("'e' not found in exponential string"); - if res.as_bytes()[idx + 1] != b'-' { - res.insert(idx + 1, '+'); - } - js_string!(res) -} diff --git a/core/engine/src/builtins/number/tests.rs b/core/engine/src/builtins/number/tests.rs index 7b2e1170f29..16187db7b11 100644 --- a/core/engine/src/builtins/number/tests.rs +++ b/core/engine/src/builtins/number/tests.rs @@ -357,6 +357,97 @@ fn num_to_string_exponential() { ]); } +// https://github.com/tc39/test262/blob/main/test/built-ins/Number/prototype/toExponential/return-values.js +#[test] +fn test262_number_prototype_toexponential_return_values() { + run_test_actions([ + TestAction::assert_eq("(123.456).toExponential(0)", js_str!("1e+2")), + TestAction::assert_eq("(123.456).toExponential(1)", js_str!("1.2e+2")), + TestAction::assert_eq("(123.456).toExponential(2)", js_str!("1.23e+2")), + TestAction::assert_eq("(123.456).toExponential(3)", js_str!("1.235e+2")), + TestAction::assert_eq("(123.456).toExponential(4)", js_str!("1.2346e+2")), + TestAction::assert_eq("(123.456).toExponential(5)", js_str!("1.23456e+2")), + TestAction::assert_eq("(123.456).toExponential(6)", js_str!("1.234560e+2")), + TestAction::assert_eq("(123.456).toExponential(7)", js_str!("1.2345600e+2")), + TestAction::assert_eq( + "(123.456).toExponential(17)", + js_str!("1.23456000000000003e+2"), + ), + TestAction::assert_eq( + "(123.456).toExponential(20)", + js_str!("1.23456000000000003070e+2"), + ), + TestAction::assert_eq("(-123.456).toExponential(0)", js_str!("-1e+2")), + TestAction::assert_eq("(-123.456).toExponential(1)", js_str!("-1.2e+2")), + TestAction::assert_eq("(-123.456).toExponential(2)", js_str!("-1.23e+2")), + TestAction::assert_eq("(-123.456).toExponential(3)", js_str!("-1.235e+2")), + TestAction::assert_eq("(-123.456).toExponential(4)", js_str!("-1.2346e+2")), + TestAction::assert_eq("(-123.456).toExponential(5)", js_str!("-1.23456e+2")), + TestAction::assert_eq("(-123.456).toExponential(6)", js_str!("-1.234560e+2")), + TestAction::assert_eq("(-123.456).toExponential(7)", js_str!("-1.2345600e+2")), + TestAction::assert_eq( + "(-123.456).toExponential(17)", + js_str!("-1.23456000000000003e+2"), + ), + TestAction::assert_eq( + "(-123.456).toExponential(20)", + js_str!("-1.23456000000000003070e+2"), + ), + TestAction::assert_eq("(0.0001).toExponential(0)", js_str!("1e-4")), + TestAction::assert_eq("(0.0001).toExponential(1)", js_str!("1.0e-4")), + TestAction::assert_eq("(0.0001).toExponential(2)", js_str!("1.00e-4")), + TestAction::assert_eq("(0.0001).toExponential(3)", js_str!("1.000e-4")), + TestAction::assert_eq("(0.0001).toExponential(4)", js_str!("1.0000e-4")), + TestAction::assert_eq( + "(0.0001).toExponential(16)", + js_str!("1.0000000000000000e-4"), + ), + TestAction::assert_eq( + "(0.0001).toExponential(17)", + js_str!("1.00000000000000005e-4"), + ), + TestAction::assert_eq( + "(0.0001).toExponential(18)", + js_str!("1.000000000000000048e-4"), + ), + TestAction::assert_eq( + "(0.0001).toExponential(19)", + js_str!("1.0000000000000000479e-4"), + ), + TestAction::assert_eq( + "(0.0001).toExponential(20)", + js_str!("1.00000000000000004792e-4"), + ), + TestAction::assert_eq("(0.9999).toExponential(0)", js_str!("1e+0")), + TestAction::assert_eq("(0.9999).toExponential(1)", js_str!("1.0e+0")), + TestAction::assert_eq("(0.9999).toExponential(2)", js_str!("1.00e+0")), + TestAction::assert_eq("(0.9999).toExponential(3)", js_str!("9.999e-1")), + TestAction::assert_eq("(0.9999).toExponential(4)", js_str!("9.9990e-1")), + TestAction::assert_eq( + "(0.9999).toExponential(16)", + js_str!("9.9990000000000001e-1"), + ), + TestAction::assert_eq( + "(0.9999).toExponential(17)", + js_str!("9.99900000000000011e-1"), + ), + TestAction::assert_eq( + "(0.9999).toExponential(18)", + js_str!("9.999000000000000110e-1"), + ), + TestAction::assert_eq( + "(0.9999).toExponential(19)", + js_str!("9.9990000000000001101e-1"), + ), + TestAction::assert_eq( + "(0.9999).toExponential(20)", + js_str!("9.99900000000000011013e-1"), + ), + TestAction::assert_eq("(25).toExponential(0)", js_str!("3e+1")), + TestAction::assert_eq("(12345).toExponential(3)", js_str!("1.235e+4")), + ]); +} + #[test] fn value_of() { // TODO: In addition to parsing numbers from strings, parse them bare As of October 2019