|
|
@ -318,28 +318,57 @@ impl Number { |
|
|
|
/// represented by these digits is rounded using string
|
|
|
|
/// represented by these digits is rounded using string
|
|
|
|
/// manipulation.
|
|
|
|
/// manipulation.
|
|
|
|
/// - Else, zeroes are appended to the string.
|
|
|
|
/// - Else, zeroes are appended to the string.
|
|
|
|
|
|
|
|
/// - Additionnally, sometimes the exponent was wrongly computed and
|
|
|
|
|
|
|
|
/// while up-rounding we find that we need an extra digit. When this
|
|
|
|
|
|
|
|
/// happens, we return true so that the calling context can adjust
|
|
|
|
|
|
|
|
/// the exponent. The string is kept at an exact length of `precision`.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// When this procedure returns, `digits` is exactly `precision` long.
|
|
|
|
/// When this procedure returns, `digits` is exactly `precision` long.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
fn round_to_precision(digits: &mut String, precision: usize) { |
|
|
|
fn round_to_precision(digits: &mut String, precision: usize) -> bool { |
|
|
|
if digits.len() > precision { |
|
|
|
if digits.len() > precision { |
|
|
|
let to_round = digits.split_off(precision); |
|
|
|
let to_round = digits.split_off(precision); |
|
|
|
let mut digit = digits.pop().unwrap() as u8; |
|
|
|
let mut digit = digits.pop().unwrap() as u8; |
|
|
|
|
|
|
|
if let Some(first) = to_round.chars().next() { |
|
|
|
|
|
|
|
if first > '4' { |
|
|
|
|
|
|
|
digit += 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for c in to_round.chars() { |
|
|
|
if digit as char == ':' { |
|
|
|
match c { |
|
|
|
// ':' is '9' + 1
|
|
|
|
c if c < '4' => break, |
|
|
|
// need to propagate the increment backward
|
|
|
|
c if c > '4' => { |
|
|
|
let mut replacement = String::from("0"); |
|
|
|
digit += 1; |
|
|
|
let mut propagated = false; |
|
|
|
break; |
|
|
|
for c in digits.chars().rev() { |
|
|
|
|
|
|
|
let d = match (c, propagated) { |
|
|
|
|
|
|
|
('0'..='8', false) => (c as u8 + 1) as char, |
|
|
|
|
|
|
|
(_, false) => '0', |
|
|
|
|
|
|
|
(_, true) => c, |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
replacement.push(d); |
|
|
|
|
|
|
|
if d != '0' { |
|
|
|
|
|
|
|
propagated = true; |
|
|
|
} |
|
|
|
} |
|
|
|
_ => {} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
digits.clear(); |
|
|
|
|
|
|
|
let replacement = if !propagated { |
|
|
|
|
|
|
|
digits.push('1'); |
|
|
|
|
|
|
|
&replacement.as_str()[1..] |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
replacement.as_str() |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
for c in replacement.chars().rev() { |
|
|
|
|
|
|
|
digits.push(c) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
!propagated |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
digits.push(digit as char); |
|
|
|
|
|
|
|
false |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
digits.push(digit as char); |
|
|
|
|
|
|
|
} else { |
|
|
|
} else { |
|
|
|
digits.push_str(&"0".repeat(precision - digits.len())); |
|
|
|
digits.push_str(&"0".repeat(precision - digits.len())); |
|
|
|
|
|
|
|
false |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -359,17 +388,24 @@ impl Number { |
|
|
|
args: &[Value], |
|
|
|
args: &[Value], |
|
|
|
context: &mut Context, |
|
|
|
context: &mut Context, |
|
|
|
) -> Result<Value> { |
|
|
|
) -> Result<Value> { |
|
|
|
let precision_var = args.get(0).cloned().unwrap_or_default(); |
|
|
|
let precision = args.get(0).cloned().unwrap_or_default(); |
|
|
|
|
|
|
|
|
|
|
|
// 1 & 6
|
|
|
|
// 1 & 6
|
|
|
|
let mut this_num = Self::this_number_value(this, context)?; |
|
|
|
let mut this_num = Self::this_number_value(this, context)?; |
|
|
|
// 2 & 4
|
|
|
|
// 2
|
|
|
|
if precision_var == Value::undefined() || !this_num.is_finite() { |
|
|
|
if precision == Value::undefined() { |
|
|
|
return Self::to_string(this, &[], context); |
|
|
|
return Self::to_string(this, &[], context); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 3
|
|
|
|
// 3
|
|
|
|
let precision = match precision_var.to_integer_or_infinity(context)? { |
|
|
|
let precision = precision.to_integer_or_infinity(context)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4
|
|
|
|
|
|
|
|
if !this_num.is_finite() { |
|
|
|
|
|
|
|
return Self::to_string(this, &[], context); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let precision = match precision { |
|
|
|
IntegerOrInfinity::Integer(x) if (1..=100).contains(&x) => x as usize, |
|
|
|
IntegerOrInfinity::Integer(x) if (1..=100).contains(&x) => x as usize, |
|
|
|
_ => { |
|
|
|
_ => { |
|
|
|
// 5
|
|
|
|
// 5
|
|
|
@ -383,7 +419,7 @@ impl Number { |
|
|
|
// 7
|
|
|
|
// 7
|
|
|
|
let mut prefix = String::new(); // spec: 's'
|
|
|
|
let mut prefix = String::new(); // spec: 's'
|
|
|
|
let mut suffix: String; // spec: 'm'
|
|
|
|
let mut suffix: String; // spec: 'm'
|
|
|
|
let exponent: i32; // spec: 'e'
|
|
|
|
let mut exponent: i32; // spec: 'e'
|
|
|
|
|
|
|
|
|
|
|
|
// 8
|
|
|
|
// 8
|
|
|
|
if this_num < 0.0 { |
|
|
|
if this_num < 0.0 { |
|
|
@ -399,10 +435,8 @@ impl Number { |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
// Due to f64 limitations, this part differs a bit from the spec,
|
|
|
|
// Due to f64 limitations, this part differs a bit from the spec,
|
|
|
|
// but has the same effect. It manipulates the string constructed
|
|
|
|
// but has the same effect. It manipulates the string constructed
|
|
|
|
// by ryu-js: digits with an optional dot between two of them.
|
|
|
|
// by `format`: digits with an optional dot between two of them.
|
|
|
|
|
|
|
|
suffix = format!("{:.100}", this_num); |
|
|
|
let mut buffer = ryu_js::Buffer::new(); |
|
|
|
|
|
|
|
suffix = buffer.format(this_num).to_string(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// a: getting an exponent
|
|
|
|
// a: getting an exponent
|
|
|
|
exponent = Self::flt_str_to_exp(&suffix); |
|
|
|
exponent = Self::flt_str_to_exp(&suffix); |
|
|
@ -413,7 +447,9 @@ impl Number { |
|
|
|
suffix.remove(n); |
|
|
|
suffix.remove(n); |
|
|
|
} |
|
|
|
} |
|
|
|
// impl: having exactly `precision` digits in `suffix`
|
|
|
|
// impl: having exactly `precision` digits in `suffix`
|
|
|
|
Self::round_to_precision(&mut suffix, precision); |
|
|
|
if Self::round_to_precision(&mut suffix, precision) { |
|
|
|
|
|
|
|
exponent += 1; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// c: switching to scientific notation
|
|
|
|
// c: switching to scientific notation
|
|
|
|
let great_exp = exponent >= precision_i32; |
|
|
|
let great_exp = exponent >= precision_i32; |
|
|
|