Date: Fri, 12 Jul 2013 19:14:39 +1000 (EST) From: Bruce Evans <brde@optusnet.com.au> To: Tijl Coosemans <tijl@coosemans.org> Cc: svn-src-head@FreeBSD.org, svn-src-all@FreeBSD.org, src-committers@FreeBSD.org, David Chisnall <theraven@FreeBSD.org>, Bruce Evans <brde@optusnet.com.au> Subject: Re: svn commit: r253215 - head/lib/msun/src Message-ID: <20130712180753.E5131@besplex.bde.org> In-Reply-To: <51DF14F9.50001@coosemans.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>
next in thread | previous in thread | raw e-mail | index | archive | help
On Thu, 11 Jul 2013, Tijl Coosemans wrote: > On 2013-07-11 22:03, Tijl Coosemans wrote: >> On 2013-07-11 21:36, David Chisnall wrote: >>> On 11 Jul 2013, at 19:52, Tijl Coosemans <tijl@coosemans.org> wrote: >>>>> @@ -227,8 +250,6 @@ double expm1(double); >>>>> double fma(double, double, double); >>>>> double hypot(double, double); >>>>> int ilogb(double) __pure2; >>>>> -int (isinf)(double) __pure2; >>>>> -int (isnan)(double) __pure2; >>>> >>>> I think they should stay for the C90 case. >>> >>> That would completely defeat the point of this entire exercise and be >>> redundant unless we aim to support a compiler that only supports C90 >>> and no GNU extensions, in which case you'll hit errors in cdefs.h, >>> long before you get to this point in an include. >> >> 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). How can that work? Even you forgot to restore the parentheses around the functions, so the above has syntax errors. Applications would have to use parentheses to get the functions (or not include <math.h>, but then it doesn't matther if it doesn't declare the functions). Applications that forget to do this will get the macros instead of the functions. If the arg type is double, then the macro will work the same as the functions. Otherwise, it has different semantics, but usually the same result except for signaling NaNs. But does old XSI really specify that parentheses must be used to get isnan() (or can be used to get an isnan() function that is specified to exist)? Old BSD has almost no specification for isnan(), and there probably isn't much old BSD code that carefully prevents use of the macro using parentheses. isnan()'s man page actually says that 3BSD introduced isinf() and isnan() functions, but these have been superseded by the macros. 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. - classification functions are specified to convert to the semantic type (remove any extra precision or exponent range) before classifying. 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. isnan(x) can safely skip the conversion, at least on x86, since conversion doesn't change the classification of NaNs. __builtin_isnan(x*x) for clang on x86 skips the conversion. __builtin_isinfinite(x*x) for clang on x86 is a combination of the above -- isnan() with no conversion and !isinf() with pessimal conversion via non-inline fabs(). I couldn't find any case where a necessary conversion is not done. Our implementation using functions gives the necessary conversions including ones that are broken for signaling NaNs. But there is no problem for with signaling NaNs for expressions like x*x, since the result of an expression with almost any operator in it can't be a signaling NaN. 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 <math.h> 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. Bruce
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20130712180753.E5131>