Skip to content

Commit

Permalink
test: Add f64.{add,sub} off by one unit tests
Browse files Browse the repository at this point in the history
The i386 x87 FPU performs all arithmetic with 80-bit extended precision
by default and in the end rounds it to the required type. This causes
issues for f64 types because results may be different than when doing
computation with 64-bit precision. f32 is not affected.
These tests presents examples of results which are different in such
case.
  • Loading branch information
chfast committed Apr 22, 2021
1 parent ece653a commit c9525d2
Showing 1 changed file with 105 additions and 0 deletions.
105 changes: 105 additions & 0 deletions test/unittests/execute_floating_point_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1282,3 +1282,108 @@ TEST(execute_floating_point, f64_store_overflow)
// Offset is 0x7fffffff + 0x80000001 => 0x100000000
EXPECT_THAT(execute(*instance, 0, {0x80000001}), Traps());
}

TEST(execute_floating_point, f64_add_round_to_even)
{
// This test presents how IEEE-754 "round to nearest, ties to even" works.
// This rounding mode is required by WebAssembly.

struct TestCase
{
double a;
double b;
double expected_sum;
};

constexpr TestCase test_cases[]{
// = - no rounding, ^ - round up, v - round down.
{0x1p0, 0x1.0000000000000p0, /* 0x2.0000000000000p0 = */ 0x1.0000000000000p1},
{0x1p0, 0x1.0000000000001p0, /* 0x2.0000000000001p0 v */ 0x1.0000000000000p1},
{0x1p0, 0x1.0000000000002p0, /* 0x2.0000000000002p0 = */ 0x1.0000000000001p1},
{0x1p0, 0x1.0000000000003p0, /* 0x2.0000000000003p0 ^ */ 0x1.0000000000002p1},
{0x1p0, 0x1.0000000000004p0, /* 0x2.0000000000004p0 = */ 0x1.0000000000002p1},
{0x1p0, 0x1.0000000000005p0, /* 0x2.0000000000005p0 v */ 0x1.0000000000002p1},
{0x1p0, 0x1.0000000000006p0, /* 0x2.0000000000006p0 = */ 0x1.0000000000003p1},
{0x1p0, 0x1.0000000000007p0, /* 0x2.0000000000007p0 ^ */ 0x1.0000000000004p1},
{0x1p0, 0x1.0000000000008p0, /* 0x2.0000000000008p0 = */ 0x1.0000000000004p1},
{0x1p0, 0x1.0000000000009p0, /* 0x2.0000000000009p0 v */ 0x1.0000000000004p1},
};

/* wat2wasm
(func (param f64 f64) (result f64)
(f64.add (local.get 0) (local.get 1))
)
*/
const auto wasm = from_hex("0061736d0100000001070160027c7c017c030201000a0901070020002001a00b");

auto instance = instantiate(parse(wasm));

for (const auto t : test_cases)
{
const auto sum = t.a + t.b;
ASSERT_EQ(sum, t.expected_sum) << std::hexfloat << t.a << " + " << t.b << " = " << sum
<< " (expected: " << t.expected_sum << ")";

EXPECT_THAT(execute(*instance, 0, {t.a, t.b}), Result(t.expected_sum));
}
}

TEST(execute_floating_point, f64_add_off_by_one)
{
// The i386 x87 FPU performs all arithmetic with 80-bit extended precision by default
// and in the end rounds it to the required type. This causes issues for f64 types
// because results may be different than when doing computation with 64-bit precision.
// f32 is not affected.
// This test presents examples of results which are different in such case.
// The testfloat tool can easily produce more such cases.

/* wat2wasm
(func (param f64 f64) (result f64)
(f64.add (local.get 0) (local.get 1))
)
*/
const auto wasm = from_hex("0061736d0100000001070160027c7c017c030201000a0901070020002001a00b");

auto instance = instantiate(parse(wasm));

constexpr auto a = 0x1.0008000008000p60;
constexpr auto b = 0x1.0000000081fffp07;
constexpr auto expected = 0x1.0008000008001p60;

// The precision on the x87 FPU can be changed to 64-bit.
// See http://christian-seiler.de/projekte/fpmath/.
/*
fpu_control_t old_fpu_cw;
_FPU_GETCW(old_fpu_cw);
const auto fpu_cw =
static_cast<fpu_control_t>((old_fpu_cw & ~_FPU_EXTENDED & ~_FPU_SINGLE) | _FPU_DOUBLE);
_FPU_SETCW(fpu_cw);
*/

EXPECT_EQ(a + b, expected); // Check host CPU.
EXPECT_THAT(execute(*instance, 0, {a, b}), Result(expected));

/*
_FPU_SETCW(old_fpu_cw);
*/
}

TEST(execute_floating_point, f64_sub_off_by_one)
{
// Same as f64_add_off_by_one, but for f64.sub.

/* wat2wasm
(func (param f64 f64) (result f64)
(f64.sub (local.get 0) (local.get 1))
)
*/
const auto wasm = from_hex("0061736d0100000001070160027c7c017c030201000a0901070020002001a10b");

auto instance = instantiate(parse(wasm));

constexpr auto a = -0x1.ffc3fffffffffp-50;
constexpr auto b = -0x1.00000000047ffp+04;
constexpr auto expected = 0x1.00000000047ffp+04;
EXPECT_EQ(a - b, expected); // Check host CPU.
EXPECT_THAT(execute(*instance, 0, {a, b}), Result(expected));
}

0 comments on commit c9525d2

Please sign in to comment.