Browse Source

Fix of instructions.rs comment, to_precision impl and rfc changes (#1135)

pull/1280/head
Nathan Royer 3 years ago committed by GitHub
parent
commit
88e888fd8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 76
      boa/src/builtins/number/mod.rs
  2. 9
      boa/src/builtins/number/tests.rs
  3. 2
      boa/src/vm/instructions.rs

76
boa/src/builtins/number/mod.rs

@ -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;

9
boa/src/builtins/number/tests.rs

@ -137,6 +137,8 @@ fn to_precision() {
var exact_precision = (123456789).toPrecision(9); var exact_precision = (123456789).toPrecision(9);
var over_precision = (123456789).toPrecision(50); var over_precision = (123456789).toPrecision(50);
var neg_precision = (-123456789).toPrecision(4); var neg_precision = (-123456789).toPrecision(4);
var neg_exponent = (0.1).toPrecision(4);
var ieee754_limits = (1/3).toPrecision(60);
"#; "#;
eprintln!("{}", forward(&mut context, init)); eprintln!("{}", forward(&mut context, init));
@ -148,6 +150,8 @@ fn to_precision() {
let exact_precision = forward(&mut context, "exact_precision"); let exact_precision = forward(&mut context, "exact_precision");
let over_precision = forward(&mut context, "over_precision"); let over_precision = forward(&mut context, "over_precision");
let neg_precision = forward(&mut context, "neg_precision"); let neg_precision = forward(&mut context, "neg_precision");
let neg_exponent = forward(&mut context, "neg_exponent");
let ieee754_limits = forward(&mut context, "ieee754_limits");
assert_eq!(infinity, String::from("\"Infinity\"")); assert_eq!(infinity, String::from("\"Infinity\""));
assert_eq!(default_precision, String::from("\"0\"")); assert_eq!(default_precision, String::from("\"0\""));
@ -160,6 +164,11 @@ fn to_precision() {
over_precision, over_precision,
String::from("\"123456789.00000000000000000000000000000000000000000\"") String::from("\"123456789.00000000000000000000000000000000000000000\"")
); );
assert_eq!(neg_exponent, String::from("\"0.1000\""));
assert_eq!(
ieee754_limits,
String::from("\"0.333333333333333314829616256247390992939472198486328125000000\"")
);
let expected = "Uncaught \"RangeError\": \"precision must be an integer at least 1 and no greater than 100\""; let expected = "Uncaught \"RangeError\": \"precision must be an integer at least 1 and no greater than 100\"";

2
boa/src/vm/instructions.rs

@ -12,7 +12,7 @@ pub enum Instruction {
/// Loads an i32 onto the stack /// Loads an i32 onto the stack
Int32(i32), Int32(i32),
/// Loads an f32 onto the stack /// Loads an f64 onto the stack
Rational(f64), Rational(f64),
/// Adds the values from destination and source and stores the result in destination /// Adds the values from destination and source and stores the result in destination

Loading…
Cancel
Save