|
|
|
@ -198,15 +198,6 @@ impl Number {
|
|
|
|
|
Err(context.construct_type_error("'this' is not a number")) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Helper function that formats a float as a ES6-style exponential number string.
|
|
|
|
|
fn num_to_exponential(n: f64) -> String { |
|
|
|
|
match n.abs() { |
|
|
|
|
x if x > 1.0 => format!("{:e}", n).replace("e", "e+"), |
|
|
|
|
x if x == 0.0 => format!("{:e}", n).replace("e", "e+"), |
|
|
|
|
_ => format!("{:e}", n), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Number.prototype.toExponential( [fractionDigits] )`
|
|
|
|
|
///
|
|
|
|
|
/// The `toExponential()` method returns a string representing the Number object in exponential notation.
|
|
|
|
@ -220,11 +211,32 @@ impl Number {
|
|
|
|
|
#[allow(clippy::wrong_self_convention)] |
|
|
|
|
pub(crate) fn to_exponential( |
|
|
|
|
this: &JsValue, |
|
|
|
|
_: &[JsValue], |
|
|
|
|
args: &[JsValue], |
|
|
|
|
context: &mut Context, |
|
|
|
|
) -> JsResult<JsValue> { |
|
|
|
|
// 1. Let x be ? thisNumberValue(this value).
|
|
|
|
|
let this_num = Self::this_number_value(this, context)?; |
|
|
|
|
let this_str_num = Self::num_to_exponential(this_num); |
|
|
|
|
let precision = match args.get(0) { |
|
|
|
|
None | Some(JsValue::Undefined) => None, |
|
|
|
|
// 2. Let f be ? ToIntegerOrInfinity(fractionDigits).
|
|
|
|
|
Some(n) => Some(n.to_integer(context)? as i32), |
|
|
|
|
}; |
|
|
|
|
// 4. If x is not finite, return ! Number::toString(x).
|
|
|
|
|
if !this_num.is_finite() { |
|
|
|
|
return Ok(JsValue::new(Self::to_native_string(this_num))); |
|
|
|
|
} |
|
|
|
|
// Get rid of the '-' sign for -0.0
|
|
|
|
|
let this_num = if this_num == 0. { 0. } else { this_num }; |
|
|
|
|
let this_str_num = if let Some(precision) = precision { |
|
|
|
|
// 5. If f < 0 or f > 100, throw a RangeError exception.
|
|
|
|
|
if !(0..=100).contains(&precision) { |
|
|
|
|
return Err(context |
|
|
|
|
.construct_range_error("toExponential() argument must be between 0 and 100")); |
|
|
|
|
} |
|
|
|
|
f64_to_exponential_with_precision(this_num, precision as usize) |
|
|
|
|
} else { |
|
|
|
|
f64_to_exponential(this_num) |
|
|
|
|
}; |
|
|
|
|
Ok(JsValue::new(this_str_num)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -244,16 +256,34 @@ impl Number {
|
|
|
|
|
args: &[JsValue], |
|
|
|
|
context: &mut Context, |
|
|
|
|
) -> JsResult<JsValue> { |
|
|
|
|
// 1. Let this_num be ? thisNumberValue(this value).
|
|
|
|
|
let this_num = Self::this_number_value(this, context)?; |
|
|
|
|
let precision = match args.get(0) { |
|
|
|
|
// 2. Let f be ? ToIntegerOrInfinity(fractionDigits).
|
|
|
|
|
Some(n) => match n.to_integer(context)? as i32 { |
|
|
|
|
x if x > 0 => n.to_integer(context)? as usize, |
|
|
|
|
_ => 0, |
|
|
|
|
0..=100 => n.to_integer(context)? as usize, |
|
|
|
|
// 4, 5. If f < 0 or f > 100, throw a RangeError exception.
|
|
|
|
|
_ => { |
|
|
|
|
return Err(context.construct_range_error( |
|
|
|
|
"toFixed() digits argument must be between 0 and 100", |
|
|
|
|
)) |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
// 3. If fractionDigits is undefined, then f is 0.
|
|
|
|
|
None => 0, |
|
|
|
|
}; |
|
|
|
|
let this_fixed_num = format!("{:.*}", precision, this_num); |
|
|
|
|
Ok(JsValue::new(this_fixed_num)) |
|
|
|
|
// 6. If x is not finite, return ! Number::toString(x).
|
|
|
|
|
if !this_num.is_finite() { |
|
|
|
|
Ok(JsValue::new(Self::to_native_string(this_num))) |
|
|
|
|
// 10. If x ≥ 10^21, then let m be ! ToString(𝔽(x)).
|
|
|
|
|
} else if this_num >= 1.0e21 { |
|
|
|
|
Ok(JsValue::new(f64_to_exponential(this_num))) |
|
|
|
|
} else { |
|
|
|
|
// Get rid of the '-' sign for -0.0 because of 9. If x < 0, then set s to "-".
|
|
|
|
|
let this_num = if this_num == 0. { 0. } else { this_num }; |
|
|
|
|
let this_fixed_num = format!("{:.*}", precision, this_num); |
|
|
|
|
Ok(JsValue::new(this_fixed_num)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// `Number.prototype.toLocaleString( [locales [, options]] )`
|
|
|
|
@ -1116,3 +1146,24 @@ impl Number {
|
|
|
|
|
!x |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Helper function that formats a float as a ES6-style exponential number string.
|
|
|
|
|
fn f64_to_exponential(n: f64) -> String { |
|
|
|
|
match n.abs() { |
|
|
|
|
x if x >= 1.0 || x == 0.0 => format!("{:e}", n).replace("e", "e+"), |
|
|
|
|
_ => format!("{:e}", n), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// 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) -> String { |
|
|
|
|
let mut res = format!("{:.*e}", prec, n); |
|
|
|
|
let idx = res.find('e').expect("'e' not found in exponential string"); |
|
|
|
|
if res.as_bytes()[idx + 1] != b'-' { |
|
|
|
|
res.insert(idx + 1, '+'); |
|
|
|
|
} |
|
|
|
|
res |
|
|
|
|
} |
|
|
|
|