-
Notifications
You must be signed in to change notification settings - Fork 34
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 f{32,64}.{ceil,floor,trunc} instructions #481
Conversation
chfast
commented
Aug 13, 2020
•
edited
Loading
edited
886b042
to
559f967
Compare
559f967
to
fdbaebd
Compare
Codecov Report
@@ Coverage Diff @@
## master #481 +/- ##
========================================
Coverage 99.56% 99.56%
========================================
Files 54 54
Lines 17015 17163 +148
========================================
+ Hits 16941 17089 +148
Misses 74 74 |
fdbaebd
to
0cc72e3
Compare
0c96df1
to
91fa494
Compare
e098abc
to
e4c2652
Compare
{ | ||
auto instance = instantiate(parse(this->get_unop_code(Instr::f32_ceil))); | ||
const auto exec = [&](auto arg) { return execute(*instance, 0, {arg}); }; | ||
|
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 there should be a specific case for -q = -0 (if -1 < -q < 0)
here.
The rules about infinity and zero matches that of C++ too (https://en.cppreference.com/w/cpp/numeric/math/ceil), but stil having tests here would be needed.
Not sure the rule requires quiet_nan, but needs a test too, because C++ just passes through any nan.
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 can the special case.
- Zeros and infinities are included in the
positive_trunc_tests
. Rules are simple and the same for all 4 rounding instructions, so I don't see point of having them duplicated everywhere. - NaNs are checked in
unop_nan_propagation
as this is common rules for all instructions (except some which ignore NaNs).
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.
Those special cases also exists for floor/trunc (actually 2 for trunc).
Will check positive_trunc_tests
.
What we could do instead of duplication, is just leave a comment that those rules are handled where.
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 added this comment in the positive_trunc_tests
.
static_assert(std::is_floating_point_v<T>); | ||
if (std::isnan(value)) | ||
return std::numeric_limits<T>::quiet_NaN(); | ||
return std::ceil(value); |
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.
From https://en.cppreference.com/w/cpp/numeric/math/ceil:
What about this? The same applies for std::floor
, and std::trunc
.
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.
We don't care if the rounding is exact or not so we can ignore this error. These are set in the errno
or similar and can be accessed by special functions, e.g. https://en.cppreference.com/w/cpp/numeric/fenv/fetestexcept.
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.
Okay, the documentation just kept calling it "floating point exception" and I thought it is an actual exception, not just a flag.
Can you leave perhaps a comment above ceil/floor that FE_INEXACT
has no bearing on the correctness of this?
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 is GCC documentation about the floating point exceptions: https://www.gnu.org/software/libc/manual/html_node/FP-Exceptions.html
They can trigger SIGFPE signal, but this is not default behavior.
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.
Apparently can be forcefully disabled with fdisableexcept
, see https://en.cppreference.com/w/cpp/numeric/fenv and https://www.gnu.org/savannah-checkouts/gnu/libc/manual/html_node/Control-Functions.html:
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.
And there's the non-call-exceptions
but that apparently also catches SIGSEGV.
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 eventually we could have an enter/leave function for setting up FP properties. "enter" in the beginning of execute
and then "enter"/"leave" wrapping on host functions.
Depends on which is more likely: heavy use of ceil/floor/trunc/round or not, because these are concerned by the rounding settings and the exceptions.
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.
Alternatively using the builtins wouldn't relieve us from this stdlib madness?
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.
Alternatively using the builtins wouldn't relieve us from this stdlib madness?
This is not portable and we are using them currently anyway. This is the implementation of std::ceil()
in libstdc++:
inline _GLIBCXX_CONSTEXPR float
ceil(float __x)
{ return __builtin_ceilf(__x); }
I think sticking to the C stdlib is fine. If someone wants to enable GNU's extension to trap on FP exceptions be my guest.
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.
If someone wants to enable GNU's extension to trap on FP exceptions be my guest.
Behaviour should be documented, and I'm leaning towards we should mention these in the README (since we don't have a complete API doc yet).
e4c2652
to
1fcc1db
Compare
|
||
// ftrunc(+q) = +0 (if 0 < +q < 1) | ||
EXPECT_THAT(exec(FP<TypeParam>::Limits::denorm_min()), Result(TypeParam{0})); | ||
EXPECT_THAT(exec(std::nextafter(TypeParam{1}, TypeParam{0})), Result(TypeParam{0})); |
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.
Should these have swapped operand: nextafter(0, 1)
e.g. the smallest number larger than 0 ?
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.
The denorm_min
is the smallest > 0.
The nextafter(0, 1)
is the largest < 1.
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.
If nextafter(0, 1)
is the largest < 1, then why do you have nextafter(1, 0)
here?
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.
Wrong comment.
nextafter(0,1)
is denorm_min
- i.e. it is the next after 0 towards 1.
nextafter(1,0)
is the largest < 1.
auto instance = instantiate(parse(this->get_unop_code(Instr::f32_ceil))); | ||
const auto exec = [&](auto arg) { return execute(*instance, 0, {arg}); }; | ||
|
||
// fceil(-q) = -0 (if -1 < -q < 0) |
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 not a fan of duplicating these cases, it might be confusing why it's tested two times.
Perhaps a comment, that it's tested in the loop over positive_trunc_tests
, but added here for explicitness, would help.
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.
These are not tested two times, these are some edge cases. I mean they are edge cases in the specification for this given function.
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.
These are the same cases as positive_trunc_tests
items
{std::nextafter(T{1}, T{0}), T{0}}
{L::denorm_min(), 0}
tested in the loop as
EXPECT_THAT(exec(-arg), Result(-expected_trunc)) << -arg << ": " << -expected_trunc;
similar in tests for other instructions
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.
Are we fine here?
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.
@gumb0 you are correct. I still prefer if we have clear single test cases for the edge cases listed in the specific for the given instruction. It is much easier to review that every case is tested.
Stuff hidden in those loops is extremely hard to review, in my opinion.
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 fine with including it, already approved.
b42242f
to
6f41e36
Compare
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.
Looks good to me.
// fceil(-q) = -0 (if -1 < -q < 0) | ||
// (also included in positive_trunc_tests, here for explicitness). | ||
EXPECT_THAT(exec(std::nextafter(-TypeParam{1}, TypeParam{0})), Result(-TypeParam{0})); | ||
EXPECT_THAT(exec(-FP<TypeParam>::Limits::denorm_min()), Result(-TypeParam{0})); |
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.
So this is -nextafter(0, 1)
?
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.
Yes.
6f41e36
to
31fa3a6
Compare
31fa3a6
to
a1532be
Compare
There is an issue with
but GCC, Release, native build is fine. |
05740dd
to
bb05127
Compare
// When rounding direction is set to FE_DOWNWARD the std::floor outputs -0 where it should +0. | ||
// The following workarounds the issue by using the fact that the sign of the output must always | ||
// match the sign of the input value. | ||
return std::copysign(result, value); |
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.
Perhaps also leave the gcc bug link here (I know it was reported for ppc, but this seems similar).
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.
That one is fixed. We need to report new one, but we need a reproducer first.
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.
ceil
that results in -0
doesn't have this problem?
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.
ceil
is tested the same way, and no issues.
Do you mean ceil()
returning +0 where it should -0? The test cases of values from [-1,-0]
are included.
#include <cmath> | ||
|
||
#pragma STDC FENV_ACCESS ON |
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 know it is possible to look what this means -- I just did that --, but perhaps nicer to leave a small warning here so that it is not removed in the future:
Note: this is important because our tests need complete control over floating point settings.
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 would comment what pragma does (enables access and modification of the floating-point environment (rounding directions in particular) ).
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.
Yes, I will add it.
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.
// and exponent 2**(mantissa digits without implicit leading 1). | ||
// The main point of using int_only_begin is to tests nearest() as for int_only_begin - 0.5 and | ||
// int_only_begin - 1.5 we have equal distance to nearby integer values. | ||
// (Integer shift is used instead of std::pow() because of the clang compiler bug). |
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.
What clang compiler bug is this? I guess we have it linked in the hackmd, do we want to link to it here too?
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 only have the clang_bug
branch for now.
// ftrunc(-q) = -0 (if -1 < -q < 0) | ||
// (also included in positive_trunc_tests, here for explicitness). | ||
EXPECT_THAT(exec(std::nextafter(-TypeParam{1}, TypeParam{0})), Result(-TypeParam{0})); | ||
EXPECT_THAT(exec(-FP<TypeParam>::Limits::denorm_min()), Result(-TypeParam{0})); |
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.
Potentially all these would be easier to follow by using nextafter(0, -1)
?
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 think so. I prefer using named values. And nextafter(0, -1)
is probably -0
.
3db9364
to
08def8c
Compare
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.
Looks good to me, but please squash the appropriate commits.
08def8c
to
b3282b4
Compare
Done. I left the "GCC bug workaround" commit separated. |