f32 && f64 tests

This commit is contained in:
Svyatoslav Nikolsky
2017-06-05 18:48:08 +03:00
parent 4fb1c80b3e
commit 1a8537dd84
5 changed files with 77 additions and 9 deletions

View File

@ -8,3 +8,5 @@ macro_rules! run_test {
}
run_test!("i32", wasm_i32);
run_test!("f32", wasm_f32);
run_test!("f64", wasm_f64);

View File

@ -10,6 +10,7 @@ use serde_json;
use test;
use parity_wasm;
use parity_wasm::interpreter::{
RuntimeValue,
ProgramInstance, ModuleInstance, ModuleInstanceInterface,
Error as InterpreterError,
};
@ -30,6 +31,18 @@ fn runtime_value(test_val: &test::RuntimeValue) -> parity_wasm::RuntimeValue {
let unsigned: u32 = test_val.value.parse().expect("Literal parse error");
parity_wasm::RuntimeValue::I32(unsigned as i32)
},
"i64" => {
let unsigned: u64 = test_val.value.parse().expect("Literal parse error");
parity_wasm::RuntimeValue::I64(unsigned as i64)
},
"f32" => {
let unsigned: u32 = test_val.value.parse().expect("Literal parse error");
parity_wasm::RuntimeValue::decode_f32(unsigned)
},
"f64" => {
let unsigned: u64 = test_val.value.parse().expect("Literal parse error");
parity_wasm::RuntimeValue::decode_f64(unsigned)
},
_ => panic!("Unknwon runtime value type"),
}
}
@ -103,6 +116,24 @@ pub fn spec(name: &str) {
}
}
},
&test::Command::AssertReturnCanonicalNan { line, ref action } | &test::Command::AssertReturnArithmeticNan { line, ref action } => {
let result = run_action(&*module, action);
match result {
Ok(result) => {
for actual_result in result.into_iter().collect::<Vec<parity_wasm::RuntimeValue>>() {
match actual_result {
RuntimeValue::F32(val) => if !val.is_nan() { panic!("Expected nan value, got {:?}", val) },
RuntimeValue::F64(val) => if !val.is_nan() { panic!("Expected nan value, got {:?}", val) },
val @ _ => panic!("Expected action to return float value, got {:?}", val),
}
}
println!("assert_return_nan at line {} - success", line);
},
Err(e) => {
panic!("Expected action to return value, got error: {:?}", e);
}
}
},
&test::Command::AssertTrap { line, ref action, .. } => {
let result = run_action(&*module, action);
match result {

View File

@ -25,6 +25,16 @@ pub enum Command {
action: Action,
expected: Vec<RuntimeValue>,
},
#[serde(rename = "assert_return_canonical_nan")]
AssertReturnCanonicalNan {
line: u64,
action: Action,
},
#[serde(rename = "assert_return_arithmetic_nan")]
AssertReturnArithmeticNan {
line: u64,
action: Action,
},
#[serde(rename = "assert_trap")]
AssertTrap {
line: u64,

View File

@ -758,7 +758,7 @@ impl Interpreter {
context
.value_stack_mut()
.pop_as::<T>()
.map(|v| v.round())
.map(|v| v.nearest())
.map(|v| context.value_stack_mut().push(v.into()))
.map(|_| InstructionOutcome::RunNextInstruction)
}

View File

@ -99,6 +99,8 @@ pub trait Float<T>: ArithmeticOps<T> {
fn trunc(self) -> T;
/// Returns the nearest integer to a number. Round half-way cases away from 0.0.
fn round(self) -> T;
/// Returns the nearest integer to a number. Ties are round to even number.
fn nearest(self) -> T;
/// Takes the square root of a number.
fn sqrt(self) -> T;
/// Returns the minimum of the two numbers.
@ -604,17 +606,40 @@ macro_rules! impl_float {
fn ceil(self) -> $type { self.ceil() }
fn trunc(self) -> $type { self.trunc() }
fn round(self) -> $type { self.round() }
fn nearest(self) -> $type {
let round = self.round();
if self.fract().abs() != 0.5 {
return round;
}
use std::ops::Rem;
if round.rem(2.0) == 1.0 {
self.floor()
} else if round.rem(2.0) == -1.0 {
self.ceil()
} else {
round
}
}
fn sqrt(self) -> $type { self.sqrt() }
// TODO
// This instruction corresponds to what is sometimes called "minNaN" in other languages.
// This differs from the IEEE 754-2008 minNum operation in that it returns a NaN if either operand is a NaN, and in that the behavior when the operands are zeros of differing signs is fully specified.
// This differs from the common x<y?x:y expansion in its handling of negative zero and NaN values.
fn min(self, other: $type) -> $type { self.min(other) }
// TODO
fn min(self, other: $type) -> $type {
if self.is_nan() || other.is_nan() {
use std::$type;
return $type::NAN;
}
self.min(other)
}
// This instruction corresponds to what is sometimes called "maxNaN" in other languages.
// This differs from the IEEE 754-2008 maxNum operation in that it returns a NaN if either operand is a NaN, and in that the behavior when the operands are zeros of differing signs is fully specified.
// This differs from the common x>y?x:y expansion in its handling of negative zero and NaN values.
fn max(self, other: $type) -> $type { self.max(other) }
fn max(self, other: $type) -> $type {
if self.is_nan() || other.is_nan() {
use std::$type;
return $type::NAN;
}
self.max(other)
}
fn copysign(self, other: $type) -> $type {
// TODO: this may be buggy for edge cases
if self.is_sign_positive() == other.is_sign_positive() {