-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement f32.demote_f64 instruction #458
Conversation
chfast
commented
Aug 4, 2020
•
edited
Loading
edited
4fe85a3
to
6636ce0
Compare
Codecov Report
@@ Coverage Diff @@
## master #458 +/- ##
========================================
Coverage 99.55% 99.55%
========================================
Files 54 54
Lines 16569 16685 +116
========================================
+ Hits 16495 16611 +116
Misses 74 74 |
7d8ad08
to
b5cef53
Compare
a94bc35
to
111b100
Compare
auto instance = instantiate(parse(wasm)); | ||
|
||
// The boundary input value that results in the infinity. | ||
constexpr double lowest_to_inf = 0x1.ffffffp127; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit confused and might be wrong, but cases p > lowest_to_inf
and p < -lowest_to_inf
are missing, and I think they are undefined behavior in C++, but defined to result in infinities in wasm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it is defined if IEEE 754 is supported (as for some other cases) because then infinities also counts.
The minimum range of representable values for a floating type is the most negative finite floating-point number representable in that type through the most positive finite floating-point number representable in that type. In addition, if negative infinity is representable in a type, the range of that type is extended to all negative real numbers; likewise, if positive infinity is representable in a type, the range of that type is extended to all positive real numbers.
https://stackoverflow.com/a/25831946/725174
Although, I'm confused how to chose nearest value between max
and infinity
...
The cases you mentions are not present because lowest_to_inf
already yields infinity, so for any p > lowest_to_inf
we get infinity too. But I can add test for nextafter(lowest_to_inf)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This answers my question.
In the default IEEE 754 round-to-nearest mode, a few double values above the maximum finite float (that is, FLT_MAX) convert to FLT_MAX. The exact limit is the number midway between FLT_MAX (0x1.fffffep127 in C99 hexadecimal representation) and the next float number that could be represented if the exponent in the single-precision format had a larger range, 0x2.0p127. The limit is thus 0x1.ffffffp127 or approximately 3.4028235677973366e+38 in decimal.
https://stackoverflow.com/a/17751145/725174
Although I wander where wasm spec handles this, if anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I finally have explanation for the lowest_to_inf
value (see comments).
I also added two cases for p > lowest_to_inf
.
e59b743
to
6facac3
Compare
constexpr double f32_limit = 0x1p128; // 2**128. | ||
|
||
// The lower boundary input value that results in the infinity. The number is midway between | ||
// f32_max and f32_limit. For this value rounding prefers infinity, because f32_limit is even. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't quite understand the last sentence. f32_limit
would indeed be even if casted to int, but for floats there's no even/odd ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But still confused, mantissa of f32_max
is even, manitssa of f32_limit
is odd
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think in both cases you are confused by the size of the f32 mantisa which is 23 bits. When written as 6 hex digits the last bit should always be 0. The mantissa full of ones is 1.fffffe
(leading 1 is implicit). Then m = 0x7fffff
and is odd.
The f32_limit
is even by definition: even_N(+-limit_N) <=> true
. But also mantissa of 1p128
is all zero therefore even.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, yes, I was confused by hex notation.
{-lowest_to_inf, -FP32::Limits::infinity()}, | ||
|
||
{std::nextafter(lowest_to_inf, FP64::Limits::infinity()), FP32::Limits::infinity()}, | ||
{-std::nextafter(lowest_to_inf, FP64::Limits::infinity()), -FP32::Limits::infinity()}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be
{-std::nextafter(lowest_to_inf, FP64::Limits::infinity()), -FP32::Limits::infinity()}, | |
{std::nextafter(-lowest_to_inf, -FP64::Limits::infinity()), -FP32::Limits::infinity()}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is the same. floats has symmetrical positive and negative ranges.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to use only single variant consistently.
6014abe
to
22adbb7
Compare
|
||
EXPECT_THAT(execute(*instance, 0, {0x1.fffffefffffffp0}), Result(0x1.fffffep0f)); // round down | ||
EXPECT_THAT(execute(*instance, 0, {0x1.fffffe0000000p0}), Result(0x1.fffffep0f)); // exact | ||
EXPECT_THAT(execute(*instance, 0, {0x1.fffffd0000001p0}), Result(0x1.fffffep0f)); // round up |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maye also add the case with double value in the middle between two floats, to show tie-to-even
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added.
@@ -92,7 +92,7 @@ struct WasmTypeName | |||
template <typename T> | |||
class execute_floating_point_types : public testing::Test | |||
{ | |||
protected: | |||
public: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to access the list of NaNs from other test suite.
b5159f4
to
28be58f
Compare
auto instance = instantiate(parse(wasm)); | ||
|
||
constexpr double f32_max = FP32::Limits::max(); | ||
ASSERT_EQ(f32_max, 0x1.fffffep127); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this assert here and not in limits? If here, can't this be made into a static_assert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be. I just want to see what the value is.
// The lower boundary input value that results in the infinity. The number is midway between | ||
// f32_max and f32_limit. For this value rounding prefers infinity, because f32_limit is even. | ||
constexpr double lowest_to_inf = (f32_max + f32_limit) / 2; | ||
ASSERT_EQ(lowest_to_inf, 0x1.ffffffp127); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't really matter, but is there a reason this should assert_eq and not static_assert?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started with static_assert
, then decided that maybe it should build anyway even if not true. I guess static_assert may be less confusing for readers.
{0x1.fffffd0000001p0, 0x1.fffffep0f}, // round up | ||
|
||
{0x1.fffff8p0, 0x1.fffff8p0f}, // exact (even) | ||
{(0x1.fffff8p0 + 0x1.fffffap0) / 2, 0x1.fffff8p0}, // tie-to-even down |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{(0x1.fffff8p0 + 0x1.fffffap0) / 2, 0x1.fffff8p0}, // tie-to-even down | |
{(0x1.fffff8p0 + 0x1.fffffap0) / 2, 0x1.fffff8p0f}, // tie-to-even down |
{0x1.fffff8p0, 0x1.fffff8p0f}, // exact (even) | ||
{(0x1.fffff8p0 + 0x1.fffffap0) / 2, 0x1.fffff8p0}, // tie-to-even down | ||
{0x1.fffffap0, 0x1.fffffap0f}, // exact (odd) | ||
{(0x1.fffffap0 + 0x1.fffffcp0) / 2, 0x1.fffffcp0}, // tie-to-even up |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
{(0x1.fffffap0 + 0x1.fffffcp0) / 2, 0x1.fffffcp0}, // tie-to-even up | |
{(0x1.fffffap0 + 0x1.fffffcp0) / 2, 0x1.fffffcp0f}, // tie-to-even up |