From owner-svn-src-all@FreeBSD.ORG Sun Jul 14 05:09:54 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]) by hub.freebsd.org (Postfix) with ESMTP id 0C45E22F; Sun, 14 Jul 2013 05:09:54 +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 48A4C31A; Sun, 14 Jul 2013 05:09:52 +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 52489D420C2; Sun, 14 Jul 2013 15:09:39 +1000 (EST) Date: Sun, 14 Jul 2013 15:09:38 +1000 (EST) From: Bruce Evans X-X-Sender: bde@besplex.bde.org To: Tijl Coosemans Subject: Re: svn commit: r253215 - head/lib/msun/src In-Reply-To: <51E1B06E.2010300@coosemans.org> Message-ID: <20130714131107.I887@besplex.bde.org> References: <201307111741.r6BHf5gQ060844@svn.freebsd.org> <51DEFEF7.4080709@coosemans.org> <7D521907-4802-4141-9A5E-40EB157A5AEF@FreeBSD.org> <51DF0FA5.4050106@coosemans.org> <51DF14F9.50001@coosemans.org> <20130712180753.E5131@besplex.bde.org> <51E1B06E.2010300@coosemans.org> MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII; format=flowed X-Optus-CM-Score: 0 X-Optus-CM-Analysis: v=2.0 cv=K8x6hFqI c=1 sm=1 a=hIML2bcmzLYA:10 a=kj9zAlcOel0A:10 a=PO7r1zJSAAAA:8 a=JzwRw_2MAAAA:8 a=Aet6fyW9sl8A:10 a=ZZKkpM5h0OA69qsTRzIA:9 a=CjuIK1q_8ugA:10 a=ebeQFi2P/qHVC0Yw9JDJ4g==:117 Cc: svn-src-head@FreeBSD.org, svn-src-all@FreeBSD.org, src-committers@FreeBSD.org, David Chisnall , Bruce Evans 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: Sun, 14 Jul 2013 05:09:54 -0000 On Sat, 13 Jul 2013, Tijl Coosemans wrote: > On 2013-07-12 11:14, Bruce Evans wrote: >> On Thu, 11 Jul 2013, Tijl Coosemans wrote: >>> On 2013-07-11 22:03, Tijl Coosemans wrote: >>>> ... >>>> isnan(double) is part of SUSv2. It should be visible when compiling with >>>> -D_XOPEN_SOURCE=500. I think you need something like this: >>>> >>>> #if (__BSD_VISIBLE || __XSI_VISIBLE <= 500) && __ISO_C_VISIBLE < 1999 >>>> int isinf(double) __pure2; >>>> int isnan(double) __pure2; >>>> #endif >>> >>> Actually this: >>> >>> #if (__BSD_VISIBLE || (defined(__XSI_VISIBLE) && __XSI_VISIBLE <= 500)) >>> && __ISO_C_VISIBLE < 1999 >> >> Remove the __ISO_C_VISIBLE part, since this is not in C90. This also >> fixes a style bug (long line). > > I shouldn't have mentioned C90. What I meant to say is the > not-C99-or-higher case which is further restricted by __BSD_VISIBLE > and __XSI_VISIBLE, but where the macros aren't defined. Maybe just put it back where it was then. We still have isnanf() under __BSD_VISIBLE and no other ifdef. It is unsorted into the __BSD_VISIBLE section for doubles. We also have finite() and finitef(). These are old aliases for isfinite(). At least they are sorted. I'd like to remove all of these. But msun still uses all of them internally. It still has a compatibility hack to misuse isnanf. This shows how hard it is to remove zombies. > These two symbols are the cause for the original problem report about > clang's cmath header (in C++11 isnan/isinf are functions returning bool > not int). Their visibility had to be constrained somehow and that > cascaded into redefining the isnan and isinf macros because they were > implemented using these functions. I think completely removing these > symbols is wrong however because it breaks the API in the specific case > of the #if above. It's safer to clean only 1 thing at a time anyway. >> I noticed some more problems in the implementation of these macros >> and others: >> - many or all of the __pure2's in the prototypes are wrong, since even >> the classification functions can have side effects for signaling >> NaNs. It is impossible to avoid these side effects for extern >> functions in some cases, since the ABI gives them. I think static >> inline functions must have the same results as extern functions, >> so compilers should pessimize inline functions as necessary to >> get the same bad results, but compilers don't do that. > > Apparently, in the 2008 version of IEEE 754 they are considered > non-computational and never generate exceptions, even for sNaN. > > The old IEEE 754-1985 only mentions isfinite and isnan and says > implementations may consider them non-arithmetic. IEEE 854-1987 says the same as 754-1987 in an appendix (recommended behaviour; not part of the standard). It says the same for copysign() and unary negation. It also says that isnan(x) is equivalent to x != x and that isfinite(x) is true precisely when -Inf < x < Inf. Many comparisons are specified to raise the invalid exception for invalid operands, and signaling NaNs can certainly be considered invalid. The != comparsion mentioned in the appendix is one of the ones that must be "unordered" and thus must raise the exception. However, comparisons are not considered as operations, so the requirement that operations with signaling NaNs raise the exception doesn't apply. The standard is unclear here. >> - classification functions are specified to convert to the semantic >> type (remove any extra precision or exponent range) before classifying. > > Yes, it makes the macros more function-like. > >> For example, if x is a double slightly larger than sqrt(DBL_MAX), and >> if double expressions are evaluated in extra exponent range, then x*x >> is finite with the extra range but infinite in the semantic type. So >> isinf(x*x) is true, and the implementation >> #define isinf(x) (fabs(x) == INFINITY) is invalid. clang on x86 gets >> __builtin_isinf(x*x) this right as a side effect of its pessimization >> of fabs() to non-inline -- parameter passing to the extern fabs() >> converts to the semantic type. Sometimes the arg is known not to have >> extra range, so no conversion is needed. > > If isinf isn't supposed to generate exceptions then it cannot use a > floating point comparison either. That would only leave bit operations. Indeed. I thought that the i387 unordered comparison instructions doesn't raise the exception for signaling NaNs, but it does. Similarly for SSE unordered comparision. Recent changes actually broke many cases that used to work :-(. Compilers don't understand this, so they generate x != x. __isnan(), __isnanf() and __isnanl() work since they use bit operations, provided calling them doesn't change the precision. So the cases that used to work on x86 except ones where the arg is float or double AND the arch is i386 AND SSE is not used AND arg passing involves copying through the i387 (the latter depends on the compiler and compiler options). All cases with signaling NaNs on x86 are now broken. >> I think C11 has new mistakes for extra precision. It specifies that >> return reduces to the semantic type, like the classification macros >> are required to do for their arg. clang -std=c11 doesn't implement >> this bug for at least: >> >> #include >> double sq(double x) { return (x*x); } >> double sq2(double x) { return (fabs(x*x); } >> >> On i386 without SSE2 (so extra precision), this generates the same code >> as with -std=c99. Squaring x gives extra precision and exponent range. >> This is not destroyed on return, so extra precision is not defeated by >> writing the squaring operation as a function. fabs() is inlined in both >> cases, so it has little effect here (no effect unless x is NaN), but I >> think even C99 doesn't permit this. If fabs() were not inline, then >> the ABI would force destruction of the extra precision and range when >> it is called, and I think C99 requires conversion to the semantic type >> for calls. > > For function parameters both C99 and C11 state that they are converted > as if by assignment meaning extra precision should be removed. This is > also required by IEEE 754-2008. Both C99 and C11 state that making a > function inline suggests calls to the function should be as fast as > possible, but I don't think this allows skipping any conversions so > even if a function is inlined the compiler should remove extra > precision. > > If a floating point value already has the right precision then > assignment and as-if-by-assignment may be seen as copy operations that > don't raise any exceptions even for sNaN. I suppose this means math > functions must always use each argument in at least one arithmetic > operation to trigger sNaNs. > > For return statements both C99 and C11 state that it's not an > assignment. Only if the type of the return expression differs from the > return type is the result converted as if by assignment. There's a > footnote that says this allows floating point values to be returned with > extra precision if there's no conversion. The extra precision can be > removed with a cast. IEEE 754-2008 requires that extra precision is > removed. The C99 requirement is bizarre. It allows sloppy code like include double sq(double x) { return (x*x); } to return the extra precision, but non-sloppy code like include double sq(double x) { return ((double_t)x*x); } that uses double_t to ensure no internal loss of extra precsision is forced to lose the extra precision on return. (double_t in this example does nothing. In a real example it would be used so that extra precision is not lost on assignment or due to compiler spilling bugs.) My understanding of the C11 regression is that the extra precision is required to be lost as if by assignment on return even when the return type is the same as the expression type. I'm surprised if IEEE 754-2008 gets this wrong. IEEE 854-1987 only has the sqrt() function. It is allowed to evaluate the result in extra precision, but only if the destination is wide enough to hold the extra precision. It is required to be provided in all supported precisions (this is presumably only from each precision to itself) and deliver correctly rounded results in all precision. i387 sqrt() probably doesn't comply. It essentially only supports long double precision, and I don't know of anything to prevent double rounding when reducing to lower precisions. > Also, fabs is somewhat special. In IEEE 754-1985 it's considered equal > to copysign(x, 1.0) which may but doesn't have to raise exceptions even > for sNaN. In IEEE 754-2008, operations that only affect the sign, like > fabs, don't raise exceptions. fabs() is interesting for C too. An IEEE binding for it shouldn't raise exceptions or lose extra precision, as if by assignment, of the arg. I think IEEE doesn't have so many problems with extra precision since its fabs is sort of type-generic. This reminds me of another problem in code that uses double_t. C99 provides null support for determining what double_t is, so for example if you have an expression of type double_t, it is hard know without using full tgmath.h or messy macros like the old ones for isnan(), what the correct functions to apply to it are, starting with fabs(). Using fabs() is required by C to lose the extra precision (but doesn't always). Using fabsl() would be wasteful if double_t is just double. Bruce