- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
This case should be defined consistently. However, ICC (19.0.1) can be made to give inconsistent results. For example, consider the following code:
#include <algorithm> inline float minwrap(float x, float y) { return std::min(x,y); } void f(float); void test() { f(std::min(-0.0f,+0.0f)); //Calls `f` with `-0.0f` f(minwrap(-0.0f,+0.0f)); //Calls `f` with `+0.0f` }
As-suggested by the comments, simply wrapping a call to `std::min(...)` (in an `inline` function no less!) is sufficient to reverse which one is returned.
- Tags:
- CC++
- Development Tools
- Intel® C++ Compiler
- Intel® Parallel Studio XE
- Intel® System Studio
- Optimization
- Parallel Computing
- Vectorization
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
According to IEEE 754 -0.0 == 0.0, therefore when both arguments to std::min are "equal", either argument can be returned.
While you would like consistency, the inline minwrap is likely changing the register assignments, and as a result, changes the selection of which zero to use for function f.
If you really require consistency, then write you own minwrap that takes into account the possibility of signed zeros, and picks the one of interest.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Can you give us a complete test case which can be compiled and created an executable? and which compiler options to use?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
BTW
I am not entirely sure if the C/C++ standard requires the literal expressed with -0.0 to generate/store an IEEE 754 0.0 with sign bit set. This may be an implementation issue that you best avoid.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
jimdempseyatthecove wrote:
According to IEEE 754 -0.0 == 0.0, therefore when both arguments to std::min are "equal", either argument can be returned.
jimdempseyatthecove wrote:The underlying issue here is that IEEE 754 and C++ and different standards. AFAIK technically speaking the C++ standard does not require the use of IEEE 754 (or operations it defines), but since basically all floating-point implementations use IEEE 754 (the main exceptions being (1) flushing denormals, especially on Intel HW, (2) obscure architectures and domain-specific encodings, and (3) software implementations), in-practice, one can usually assume that C++ `float` and `double` are essentially IEEE 754-compliant.
I am not entirely sure if the C/C++ standard requires the literal expressed with -0.0 to generate/store an IEEE 754 0.0 with sign bit set.
You are correct that IEEE 754 does allow either argument to be returned for the analogous functions defined by that standard:
IEEE 754-2008 (§11) wrote:However, this is not the C++ standard. The C++ standard says (of `std::min(...)`):
Do not depend on the sign of a zero result [...] for minNum(x, y), maxNum(x, y), minNumMag(x, y), or maxNumMag(x, y) when x and y are equal.
C++ N4810 Working Draft (§25.7.8) wrote:
Returns the first argument when the arguments are equivalent.
C++ N4810 Working Draft (§25.7) wrote:So AFAICT, since if both arguments are ±0, the comparisons are `false`, their complements, and subsequent conjunction, are `true`, and so the arguments are equivalent, and the first argument must be returned.
The equivalence to which we refer is not necessarily an operator==, but an equivalence relation induced by the strict weak ordering. That is, two elements a and b are considered equivalent if and only if !(a < b) && !(b < a).
For reference, MSVC is also inconsistent, while Clang and GCC both seem to define it the way I described.
It's worth noting that NaN arguments can (and should) be carefully considered in this way as-well. Compilers do seem to be more-consistent for these cases, although while I'm here, I should mention that ICC (and others) regularly miss fairly simple optimization opportunities on that.
(Also, hopefully it's obvious, but all of this applies to `std::max(...)` analogously as-well.)
jimdempseyatthecove wrote:That's my guess as-well. Happily, the above matches quite conveniently to the ASM, since `v?min[sp][ds]` returns `SRC2` if both arguments are ±0 or if either is NaN. So, pass them in the opposite order as for `std::min(...)` and there should be no performance penalty.
While you would like consistency, the inline minwrap is likely changing the register assignments, and as a result, changes the selection of which zero to use for function f.
Viet Hoang (Intel) wrote:Adapting the example I gave above is almost trivial, but here you go:
Can you give us a complete test case which can be compiled and created an executable? and which compiler options to use?
#include <algorithm> #include <cstdio> inline float minwrap(float x, float y) { return std::min(x,y); } void f(float x) { printf("%f\n",(double)x); } int main(int,char*[]) { f(std::min(-0.0f,+0.0f)); //Calls `f` with `-0.0f` f(minwrap (-0.0f,+0.0f)); //Calls `f` with `+0.0f` return 0; }
Compile it with "-O3".
Best,
Ian
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page