From owner-svn-src-all@FreeBSD.ORG Mon Jul 29 13:30:20 2013 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTP id CAC129C0; Mon, 29 Jul 2013 13:30:20 +0000 (UTC) (envelope-from brde@optusnet.com.au) Received: from mail107.syd.optusnet.com.au (mail107.syd.optusnet.com.au [211.29.132.53]) by mx1.freebsd.org (Postfix) with ESMTP id 742F62633; Mon, 29 Jul 2013 13:30:20 +0000 (UTC) Received: from c122-106-156-23.carlnfd1.nsw.optusnet.com.au (c122-106-156-23.carlnfd1.nsw.optusnet.com.au [122.106.156.23]) by mail107.syd.optusnet.com.au (Postfix) with ESMTPS id 6464DD44035; Mon, 29 Jul 2013 23:30:15 +1000 (EST) Date: Mon, 29 Jul 2013 23:30:14 +1000 (EST) From: Bruce Evans X-X-Sender: bde@besplex.bde.org To: David Schultz Subject: Re: svn commit: r253215 - head/lib/msun/src In-Reply-To: <20130729070517.GA3192@zim.MIT.EDU> Message-ID: <20130729210519.G1146@besplex.bde.org> References: <201307111741.r6BHf5gQ060844@svn.freebsd.org> <20130729070517.GA3192@zim.MIT.EDU> MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII; format=flowed X-Optus-CM-Score: 0 X-Optus-CM-Analysis: v=2.1 cv=Yos2GeoX c=1 sm=1 tr=0 a=ebeQFi2P/qHVC0Yw9JDJ4g==:117 a=PO7r1zJSAAAA:8 a=hIML2bcmzLYA:10 a=kj9zAlcOel0A:10 a=JzwRw_2MAAAA:8 a=Aet6fyW9sl8A:10 a=BKZECA6cT18e-IigmXQA:9 a=YIYcuchHDKjwy6PG:21 a=OZ92-9CpP4R_YQa2:21 a=CjuIK1q_8ugA:10 Cc: svn-src-head@freebsd.org, svn-src-all@freebsd.org, src-committers@freebsd.org, David Chisnall X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 29 Jul 2013 13:30:20 -0000 On Mon, 29 Jul 2013, David Schultz wrote: > On Thu, Jul 11, 2013, David Chisnall wrote: >> +static __inline int >> +__inline_isnan(double __x) >> +{ >> + >> + return (__x != __x); >> +} >> + >> +static __inline int >> +__inline_isnanf(float __x) >> +{ >> + >> + return (__x != __x); >> +} >> + >> +static __inline int >> +__inline_isnanl(long double __x) >> +{ >> + >> + return (__x != __x); >> +} > > This has already been covered at greater length, but I believe > this part is incorrect. The correctness of this part has always been covered at greater length :-). We use simple "!=" tests because there is no way to get correct behaviour and the simple test is more correct than most, including the extern function implementation, and faster than all. Even greater length follows :-). > Relational operators can raise an invalid > exception when one of the arguments is a NaN -- even a quiet NaN. > Raising an exception is optional in C99 (7.12.14) and required in > IEEE-754... in practice, it tends to be platform- and compiler- > specific. IEEE-754-2008 actually requires the '=' and '!=' comparisions (with that spelling (or with the alternative spellins '?<>', or 'NOT=' for '!=')) to _not_ generate an exception for quiet NaNs. It has an unordered-signaling and and unordered-quiet variant for all comparisions. The equality and inequality signaling comparisions are so unusual that IEEE-754-2008 doesn't even give normal spellings for their abbreviations in its tables. The tables in IEEE-854-1987 seem to be better, and give these as '<>' and 'NOT<>'. Howver, for inequality predicates like '<', it is the unordered-signaling variants that have the normal spelling, with unordered-quiet '<' having the special spelling '?<'. I first thought that the 2 types of comparisons were new in 2008, but now see that they were in the IEEE-854-1987 with only a small difference in emphasis. The unordered-quiet variants are intended to be used in code that takes into account the possibility of NaNs. That is almost no code. I had misremembered this (signaling for the usual case) as being a design error, because real hardware like i387 and SSE has limited support for signaling. Actually, i387 and SSE have full support for both cases (FCOM and COM give the signaling behaviour, and FUCOM and UCOM give the quiet behaviour). The only documentation that I can find now that says that FCOM should never be used is where I probably learned it from in _The 8086/386 Architecture_ by Morse et al. This says: Since the 8087 was introduced, the floating-point standard specified that comparisons involving quiet NaNs should return a result of \"unordered,\" with no exception raised. Since the FUCOM instruction raises the invalid exception, in this case it fails to meet the standard. So the _unordered comparison_ instructuctions FUCOM, FUCOMP, and FUCOMPP were added to the 387. [...] [Recommendation to use FCOM in simple programs and let the exception handler deal with NaN results. Use FUCOM if we want to handle NaNs and the unordered condtion ourselves.] But the standard actually requires both, at least in 1987 when Morse's book was published. C99 requires the signaling variants, except possiblly for the equality predicates. For inequality, islessgreater() gives IEEE quiet inequality '?<>' or !'=' except it is underspecified for NaNs. You can't get IEEE quiet equality by inverting this since it must not be inverted for NaNs. You can build quiet IEEE equality and using this and isunordered(), but the logic for this is complicated and it would be hard for compilers to turn it back into direct IEEE quiet unordered equality comparisons. C99 clarifies the intended behaviour in footnote 194, but footnotes are not part of the standard: %%% 194IEC 60559 requires that the built-in relational operators raise the invalid exception if the operands compare unordered, as an error indicator for programs written without consideration of NaNs; the result in these cases is false. %%% So '=' and '!=' should always give 'false' for NaNs, and the macros should have the same behaviour. x86 C compilers understand none of this. They seem to have started avoiding using FCOM since before Morse's book came out, and now always generate FUCOM/UCOM. So they always give unordered comparisons. So the support for "simple" programs is broken by default, and there is no way to get it (no flag for stricter IEEE conformance?), and the C comparison macros are useless in unportable x86 code since they generate the same unordered comparison instructions as direct comparisons. More usefully, the implemenation can be unportable, so it can depend on these bugs in . Using the x != x comparision without an ifdef is perhaps too x86-specific, but the other arches are likely to be even more broken no one cares about them. I just tested the following program on amd64, ia64 and sparc64. It fails to raise invalid for the comparision of NaNs on all of them: #include #include #include double z; int main(void) { z = z / z; printf("%d\n", fetestexcept(FE_INVALID)); feclearexcept(FE_INVALID); if (z >= z) printf("equal\n"); printf("%d\n", fetestexcept(FE_INVALID)); return (0); } > That is the whole reason the is* macros are defined by the > standard in the first place, and also why we didn't use the > trivial implementation above. The is* macros are required to not > raise an exception. > > P.S. It would be great if clang implemented the FENV_ACCESS pragma > and provided an intrinsic that produced a fast inline isnan() when > the pragma is off, and the full, correct one when the pragma is on. Not really great, since the ifdefs to actually use this would be messy. It would take an especially unportable pragma to switch between quiet (technically broken) and signaling comparisons, and and this pragma would be especially useless on x86 since both types of comparisons are supported directly in hardware so there are no efficiency advantages from using the technically broken one. Anwyay, FENV_ACCESS is irrelevant, since the fast inline isnan() is independent of it. The fast inline isnan() already exists as __builtin_isnan() for clang and as plain isnan() or __builtin_isnan() for gcc-4+. But ifdefs for this would be too messy. Exceptions for comparing NaNs are more interesting if trapping of the invalid exception is enabled. Otherwise, they no difference except for non-simple programs that are aware enough of NaNs to test for the invalid exception at least once. Even non-simple programs shouldn't be handling NaNs so tenderly as to avoid subsequent exceptions for them, as they were apparently expected to do in Morse's book. I think exceptions for comparison are actually most useful for the non-simple programs. Simple programs would raise invalid when they generate a NaN and never re-raise it, and only notice if it is trapped. Non-simple programs might clear the invalid exception occasionally and prefer it to be raised by comparisons later, instead of ensuring that NaNs don't propagate. And if they look at their data too much so as to weed out NaNs, then exceptions for comparisons are useful for detecting cases that the weeding missed. Bruce