On Wed, 10 Jul 2013, Garrett Wollman wrote: > <<On Wed, 10 Jul 2013 22:12:59 +0200, Tijl Coosemans <tijl_at_freebsd.org> said: > >> I think isnan(double) and isinf(double) in math.h should only be >> visible if (_BSD_VISIBLE || _XSI_VISIBLE) && __ISO_C_VISIBLE < 1999. >> For C99 and higher there should only be the isnan/isinf macros. > > I believe you are correct. POSIX.1-2008 (which is aligned with C99) > consistently calls isnan() a "macro", and gives a pseudo-prototype of > > int isnan(real-floating x); Almost any macro may be implemented as a function, if no conforming program can tell the difference. It is impossible for technical reasons to implement isnan() as a macro (except on weird implementations where all real-floating types are physically the same). In the FreeBSD implementation, isnan() is a macro, but it is also a function, and the macro expands to the function in double precision: % #define isnan(x) \ % ((sizeof (x) == sizeof (float)) ? __isnanf(x) \ % : (sizeof (x) == sizeof (double)) ? isnan(x) \ % : __isnanl(x)) I don't see how any conforming program can access the isnan() function directly. It is just as protected as __isnan() would be. (isnan)() gives the function (the function prototype uses this), but conforming programs can't do that since the function might not exist. Maybe some non-conforming program like autoconfig reads <math.h> or libm.a and creates a bug for C++. The FreeBSD isnan() implementation would be broken by removing the isnan() function from libm.a or ifdefing it in <math.h>. Changing the function to __isnan() would cause compatibility problems. The function is intentionally named isnan() to reduce compatibility problems. OTOH, the all of the extern sub-functions that are currently used should bever never be used, since using them gives a very low quality of implementation: - the functions are very slow - the functions have names that confuse compilers and thus prevent compilers from replacing them by builtins. Currently, only gcc automatically replaces isnan() by __builtin_isnan(). This only works in double precision. So the FreeBSD implementation only works right in double precision too, only with gcc, __because__ it replaces the macro isnan(x) by the function isnan(x). The result is inline expansion, the same as if the macro isnan() is replaced by __builtin_isnan(). clang never does this automatic replacement, so it generates calls to the slow library functions. Other things go wrong for gcc in other precisions: - if <math.h> is not included, then isnan(x) gives __builtin_isnan((double)x). This sort of works on x86, but is low quality since it is broken for signaling NaNs (see below). One of the main reasons reason for the existence of the classification macros is that simply converting the arg to a common type and classifying the result doesn't always work. - if <math.h> is not included, then spelling the API isnanf() or isnanl() gives correct results but a warning about these APIs not being declared. These APIs are nonstandard but are converted to __builtin_isnan[fl] by gcc. - if <math.h> is included, then: - if the API is spelled isnan(), then the macro converts to __isnanf() or __isnanl(). gcc doesn't understand these, and the slow extern functions are used. - if the API is spelled isnanf() or isnanl(), then the result is correct and the warning magically goes away. <math.h> declares isnanf(), but gcc apparently declares both iff <math.h> is included. gcc also optimizes isnanl() on a float arg to __builtin_isnanf(). - no function version can work in some cases, because any function version may have unwanted side effects. This is another of the main reason for the existence of these and other macros. The main unwanted side effect is signaling for signaling NaNs. C99 doesn't really support signaling NaNs, even with the IEC 60559 extensions, so almost anything is allowed for them. But IEEE 854 is fairly clear that isnan() and classification macros shouldn't raise any exceptions. IEEE 854 is even clearer that copying values without changing their representation should (shall?) not cause exceptions. But on i387, just loading a float or double value changes its representation and generates an exception for signaling NaNs, while just loading a long double value conforms to IEEE 854 and doesn't change its representation or generate an exception. Passing of args to functions may or may not load the values. ABIs may require a change of representation. On i387, passing of double args should go through the FPU for efficiency reasons, and this changes the representation twice to not even get back to the original (for signaling NaNs, it generates an exception and sets the quiet bit in the result; thus a classification function can never see a signaling NaN in double precision). So a high quality inplementation must not use function versions, and it must also use builtins that don't even load the values into normal FP registers if this might cause an exception or change the values. Both gcc's and clang's builtins are broken on i387 (i386 or amd64 -m32) since the do load the value. In view of all these bugs, the best available implementation of isnan(x) is ((x) != (x)) (using an unportable statement-expression to avoid multiple evaluation). The known bugs in this implementation are: - it will load the value and thus give unwanted exceptions for signaling NaNs in some cases. But compiler builtins are no better. (IEEE 854 has a lot to say about exceptions for comparison operators, and the builtin comparison operators exist in C99 for related reasons. IIRC, the comparison operators are useless with IEEE 854 conformance, since ordinary comparison operators then do the right thing. The details are unclear, but I couldn't find any cases where gcc or clang on x86 do the wrong thing or do different things for the builtin comparison operators. For ((x) != (x)), they generate an "unordered" comparison and this is the right thing for isnan(). Their builtin isnan()s generate exactly the same code as for ((x) != (x)).) - it may be broken by flags like -ffast-math that turn off IEEE conformance. BruceReceived on Thu Jul 11 2013 - 02:21:43 UTC
This archive was generated by hypermail 2.4.0 : Wed May 19 2021 - 11:40:39 UTC