**Document:** WG14 N1271

**Submitter:** Fred Tydeman (USA)

**Submission Date:** 2007-10-10

**Source:** WG14

**Version:** 1.0

**Date:** 2007-10-10

**Subject:** fpclassify() with binary and decimal FP

**Problem**

An implementation that supports both binary and decimal IEEE-754 floating-point (FP) has problems with the FP classification macros, the FP comparison macros, and possibly the <tgmath.h> functions.

C99 in section 7.12.3.1 has a possible definition of fpclassify() macro:

#define fpclassify(x) \

((sizeof (x) == sizeof (float)) ? __fpclassifyf(x) : \

(sizeof (x) == sizeof (double)) ? __fpclassifyd(x) : \

__fpclassifyl(x))

Extending this to work with both binary FP and decimal FP has problems. It will not work if sizeof(float) == sizeof(_Decimal32) or if sizeof(double) == sizeof(_Decimal64); both of which are true for the common 32-bit based implementations.

The rationale for the decimal FP paper (N1242) says (in 9.3)
this problem is solved with compiler magic. That gives no
help to third party library vendors who wish to write
portable code that will be usable with multiple compilers.
That section also mentions the idea of adding a language
operator like *type_of* or *radix_of*.

**Discussion**

Since types are not known during the preprocessor phase of translation, a preprocessor based solution will not work.

gcc and EDG have a typeof() macro that returns a string that can be used to create source code with. For example,

typeof(x) y = x;

declares y to be of the same type as x and initializes y with the value of x.

It is not clear how one uses this facility to help with fpclassify(). If the string returned by this macro could be compared, e.g., strcmp(typeof(x),typeof(double)), then it could be used.

EDG has several builtin macros for working with type-generic function macros. One is for a mix of 3 float and 3 complex types. Another is for a mix of 12 fixed point types. They could add one for a mix of 3 binary float and 3 decimal float types. I believe that this is how one of their macros is used:

#define fpclassify(x) \

__generic(x,,, __fpclassifyd(x), __fpclassifyf(x), __fpclassifyl(x),,,)(x)

A portable compiler magic solution is needed. Below are two
possible *type_of* solutions, followed by a
*radix_of* solution.

A *type_of* solution is a translation time function that
returns different integers for different type specifiers. It
needs to work with expressions and should also work for
types.

void, bit-fields, struct, union, arrays, pointers and functions need not be supported.

Type qualifiers (const, volatile and restrict) should be ignored.

Storage-class specifiers, such as register, should be ignored.

The following solution hides actual return values, but requires that the function work with both expressions and types. Since the actual return values are not documented, this makes it easy for an implementation to extend with other types. It also has the least name space pollution.

#define fpclassify(x) \

((type_of(x) == type_of(float)) ? __fpclassf(x) : \

(type_of(x) == type_of(double)) ? __fpclassd(x) : \

(type_of(x) == type_of(long double)) ? __fpclassl(x) : \

(type_of(x) == type_of(_Decimal32)) ? __fpclassf32(x) : \

(type_of(x) == type_of(_Decimal64)) ? __fpclassd64(x) : \

(type_of(x) == type_of(_Decimal128)) ? __fpclassl128(x) : \

__fpclass_bad(x))

The following solution works with just expressions, but requires that the return values have names. These names could be either preprocessor symbols or enums.

#define fpclassify(x) \

((type_of(x) == TYPE_float) ? __fpclassf(x) : \

(type_of(x) == TYPE_double) ? __fpclassd(x) : \

(type_of(x) == TYPE_long_double) ? __fpclassl(x) : \

(type_of(x) == TYPE_Decimal32) ? __fpclassf32(x) : \

(type_of(x) == TYPE_Decimal64) ? __fpclassd64(x) : \

(type_of(x) == TYPE_Decimal128) ? __fpclassl128(x) : \

__fpclass_bad(x))

The idea of *radix_of* would need to be combined with
*sizeof* in the fpclassify() macro. It seems like using
both would be make for a more verbose fpclassify(). Having
more operations to determine the function to call might also
make it harder to do constant folding (so as to reduce the
code down to zero runtime tests and a single function call).

#define fpclassify(x) \

(((2==radix_of(x)) && (sizeof(x)==sizeof(float))) ? __fpclassf(x) : \

((2==radix_of(x)) && (sizeof(x) == sizeof(double))) ? __fpclassd(x) : \

((2==radix_of(x)) && (sizeof(x) == sizeof(long double))) ? __fpclassl(x) : \

((10==radix_of(x)) && (sizeof(x) == sizeof(_Decimal32))) ? __fpclassf32(x) : \

((10==radix_of(x)) && (sizeof(x) == sizeof(_Decimal64))) ? __fpclassd64(x) : \

((10==radix_of(x)) && (sizeof(x) == sizeof(_Decimal128))) ? __fpclassl128(x) : \

__fpclass_bad(x))

Thinking very long term, we may need a facility that will be able to work with ( binary, decimal ) x ( float, double, long double ) x ( real, complex, imaginary ), or 18 types. To make matters worse, a two argument function like pow(x,y), would have 18*18, or 324, combinations to deal with. This is not a problem now, since the decimal FP work is not suggesting any support for complex or imaginary, and prohibits mixing binary FP with decimal FP.

**Suggested change for C1x**