-
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
test: Add floating-point utils #454
Conversation
7e223b6
to
e694fc7
Compare
Codecov Report
@@ Coverage Diff @@
## master #454 +/- ##
========================================
Coverage 99.50% 99.51%
========================================
Files 52 54 +2
Lines 14939 15120 +181
========================================
+ Hits 14865 15046 +181
Misses 74 74 |
test/utils/floating_point_utils.hpp
Outdated
} | ||
|
||
template <typename T, typename = std::enable_if<std::is_floating_point_v<T>>> | ||
T nan(uint64_t payload = FPUtils<T>::CanonicalNaNPayload) noexcept |
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.
Couldn't we use std::nan
/std::nanf
for this? They seem to be able to set a payload, 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.
True, but they do it in an implementation-defined manner related to printing NaN "values". Maybe there is a way to find a combination of chars that gives wasm canonical and arithmetic NaNs, but I decided to go with my own NaN inspection helpers. Also to learn the internal structure of floats.
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 some unit tests comparing with std::nan()
. It produces only quiet (aka arithmetic) nans. I prefer testing others (i.e. signaling) nans too.
e34d942
to
d19a6fb
Compare
9dc8ab7
to
f1de6c7
Compare
Tests for |
8c71f3e
to
2b35f84
Compare
Added some tests for that. They are also used in |
else if constexpr (std::is_same_v<value_type, double>) | ||
return arg.value.f64 == value; | ||
if constexpr (std::is_floating_point_v<value_type>) | ||
return arg.value.template as<value_type>() == fizzy::test::FP{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.
This C++ syntax of template
just to be able to use as<value_type>
is terribly, but it is what it is.
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 beneficial over the previous one, btw? Wouldn't it be simpler here just comparing the binary representation?
I understand the FP class will come handy later.
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 FP
is comparing binary representation.
The template
is a mandatory hint to the compiler that the <
in as<value_type>
is a template bracket. Fortunately, GCC is giving nice hints when you forget it.
return std::isnan(value) ? (as_uint() & mantissa_mask) : 0; | ||
} | ||
|
||
/// Build the NaN value with the given payload. |
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.
Is this the canonical one?
Perhaps add a comment it is canonical by default? Though from a reading-tests-perspective perhaps having nan()
and cnan()
or canonical_nan()
is better.
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 I can make the argument explicit, as they are coming in groups, e.g. canon, canon+1, 1, ...
.
Out of curiousity: could we add tests to the main cmake (next to the bigendian tests) which ensures the platform has the proper IEEE-754 / NaN support? |
See #462. |
EXPECT_EQ(FP(-FP64::Limits::denorm_min()).as_uint(), 0x800'0000000000001); | ||
EXPECT_EQ(FP(1.0).as_uint(), 0x3FF'0000000000000); | ||
EXPECT_EQ(FP(-1.0).as_uint(), 0xBFF'0000000000000); | ||
EXPECT_EQ(FP(std::nextafter(1.0, 0.0)).as_uint(), 0x3FE'FFFFFFFFFFFFF); |
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.
Damn these helpers, so this is the 1.0 - <smallest representable value>
(e.g. 0.999999999999999...)
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.
Not exactly. This is the next representable value after 1.0
towards 0.0
. But should be something like 0.9999...
.
template <typename DstT, typename SrcT> | ||
DstT bit_cast(SrcT x) noexcept | ||
{ | ||
DstT z; |
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.
x, z are an interesting choice of combination :)
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.
Took it from Go's BigInt, where z
is always the return value. Want me to change 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.
It is fine, does not matter too much. Would have used something like from/to, a/b, x/y, in/out -- but many of these could be subject to some clashes with certain compilers, so not worth the hassle.
const auto one = 1.0; | ||
const auto inf = FP64::Limits::infinity(); | ||
const auto cnan = FP64::nan(FP64::canon); | ||
const auto snan = FP64::nan(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.
What is snan?
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.
signaling nan
template <typename T> | ||
struct FP | ||
{ | ||
static_assert(std::is_same_v<T, float> || std::is_same_v<T, double>); |
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.
Hm, do you also want to add a static_assert
for float/double being iee754 or fine as we have it in value.h and/or the limits check below?
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 static_assert(Limits::is_iec559);
does exactly that, except only when FP
is instantiated.
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.
Haven't conducted a 100% thorough review (depending on @gumb0 here), but this looks okay to me.
constexpr auto qnan = FP32::Limits::quiet_NaN(); | ||
|
||
EXPECT_EQ(FP(0.0f).nan_payload(), 0); | ||
EXPECT_EQ(FP(FP32::Limits::signaling_NaN()).nan_payload(), 0x200000); |
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 exact payloads of Limits::quiet_NaN()
and Limits::signaling_NaN()
are implementation defined, aren't they? So maybe not worth it to put it into tests? Or maybe add a comment about 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.
Moved to separate tests.
test/utils/floating_point_utils.hpp
Outdated
friend bool operator!=(T a, FP b) noexcept { return FP{a} != b; } | ||
}; | ||
|
||
FP(uint64_t)->FP<double>; |
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.
Nitpick: these two could be ordered float, then double, like using
below
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.
Reordered.
These are helpers mostly to inspect and build NaN "values". Wasm has concepts of canonical and arithmetic NaNs, which are not present in C/C++.
Some future usage examples: