[Cfp-interest] constant rounding modes and type-generic macros

Jim Thomas jaswthomas at sbcglobal.net
Wed Apr 25 13:09:08 PDT 2012


This example illustrates the issue in question:

Given

#define __STDC_WANT_IEC_60559_BFP__
#include <tgmath.h> 
#undef cbrtf
#pragma STDC FENV_ROUND FE_UPWARD
float f;

then should a call

… cbrt(f) ...

be affected by the constant rounding mode (hence rounded upward)?

According to our draft (7.6.2), "#undef cbrtf" suppresses macro replacement for calls to cbrtf, so calls to cbrtf are not affected. C11 7.25 says that use of a type-generic macro invokes the determined function. In this case, use of cbrt(f) invokes cbrtf(f). I think this implies the call is not affected.

Would it be better to change the spec so that tgmath macros are always subject to constant rounding modes? Some facility (to disable the affect of a constant rounding mode) would be lost. Which approach would be less surprising to user? Clearer for implementers?

-Jim

On Apr 19, 2012, at 2:25 PM, Joel C. Salomon wrote:

> On 04/07/2012 08:26 PM, Jim Thomas wrote:
>> This is intended as a summary of discussion the past few weeks about how
>> constant rounding modes affect functions invoked by type-generic macros
>> in <tgmath.h>. We'll discuss and try to wrap up this issue at our April
>> meeting. 
> 
> I’ve been considering how it may be possible to implement the
> constant-rounding-mode magic required in CFP1’s Table 2. We briefly
> discussed the issue at today’s phone conference, but it’s not yet ready
> to be wrapped up. Here are my thoughts on the matter.
> 
> There are two sorts of implementations we need to discuss: those with
> true static constant rounding modes (one of which is "dynamic"), and
> those with only dynamic modes, where 754-2008’s constant modes must be
> emulated.
> 
> On either sort of system, it is necessary that the compiler know that
> the functions in Table 2 are “operations affected by constant rounding
> mode”—but this compiler knowledge must be forgotten if macro replacement
> has been suppressed.
> 
> For the purposes of argument, I will propose syntax used by a
> hypothetical compiler:
> • The __constrndfunc operator, when applied to a function
>  type, yields the same function but with compiler knowing
>  that it is affected by constant rounding mode.
> • The __constrndmode identifier behaves as a constant
>  expression, yielding the current static rounding mode if
>  one is in existence, FE_DYNAMIC otherwise.
> 
> The first feature will suffice on systems where the Table 2 functions
> are implemented only once each, rounding according to a dynamic mode:
> 
>    // <math.h>
>    /* these functions round according to the current (possibly
>     * default) dynamic mode and are used when macro replacement
>     * has been suppressed
>     */
>    float cbrtf(float);
>    double cbrt(double);
> 
>    #define cbrtf(x) __constrndfunc cbrtf(x)
>    #define cbrt(x)  __constrndfunc cbrt(x)
> 
>    // <tgmath.h>
>    #undef cbrt
>    #define cbrt(x) _Generic((x),       \
>        float:   __constrndfunc cbrtf,  \
>        default: __constrndfunc cbrt    \
>        )(x)
> 
> The second feature is intended for systems where Table 2 functions have
> multiple implementations, for each rounding mode:
> 
>    // <math.h>
>    /* these functions round according to the current (possibly
>     * default) dynamic mode and are used when macro replacement
>     * has been suppressed
>     */
>    float cbrtf(float);
>    double cbrt(double);
> 
>    float __cbrtf_downward(float),   __cbrtf_tonearest(float),
>          __cbrtf_towardzero(float), __cbrtf_upward(float);
>    double __cbrt_downward(double),   __cbrt_tonearest(double),
>           __cbrt_towardzero(double), __cbrt_upward(double);
> 
>    #define __constrndfunc(func) ( \
>        __constrndmode == FE_DYNAMIC    ? func                  : \
>        __constrndmode == FE_DOWNWARD   ? __##func##_downward   : \
>        __constrndmode == FE_TONEAREST  ? __##func##_tonearest  : \
>        __constrndmode == FE_TOWARDZERO ? __##func##_towardzero : \
>        __constrndmode == FE_UPWARD     ? __##func##_upward     : \
>        0 )
> 
>    #define cbrtf(x) __constrndfunc(cbrtf)(x)
>    #define cbrt(x)  __constrndfunc(cbrt)(x)
> 
>    // <tgmath.h>
>    #undef cbrt
>    #define cbrt(x) _Generic((x),       \
>        float:   __constrndfunc(cbrtf), \
>        default: __constrndfunc(cbrt)   \
>        )(x)
> 
> This model makes the most sense to me, but I’m open to critique.
> 
> Jim, in his email, suggested that the current CFP spec does not actually
> allow for this implementation; his reading has it that the float
> selection-branch of the <tgmath.h> cbrt() macro should be defined in
> terms of the cbrtf() macro; i.e. (per the first form):
> 
>    // <math.h> as above
> 
>    // <tgmath.h>
>    #undef cbrt
>    #define cbrt(x) _Generic((x),       \
>        float:   cbrtf(x),  /* this */  \
>        default: __constrndfunc cbrt(x) \
>        )
> 
> or (per the second form):
> 
>    // <math.h>
>    float cbrtf(float), __cbrtf_downward(float), __cbrtf_upward(float),
>          __cbrtf_tonearest(float), __cbrtf_towardzero(float);
>    double cbrt(double), __cbrt_downward(double), __cbrt_upward(double),
>           __cbrt_tonearest(double), __cbrt_towardzero(double);
> 
>    #define __constrndfunc(func, x) ( \
>        __constrndmode == FE_DYNAMIC    ? func(x)                 : \
>        __constrndmode == FE_DOWNWARD   ? __##func##_downward(x)  : \
>        __constrndmode == FE_TONEAREST  ? __##func##_tonearest(x) : \
>        __constrndmode == FE_TOWARDZERO ? __##func##_towardzero(x): \
>        __constrndmode == FE_UPWARD     ? __##func##_upward(x)    : \
>        SNAN )
> 
>    #define cbrtf(x) __constrndfunc(cbrtf, (x))
>    #define cbrt(x)  __constrndfunc(cbrt, (x))
> 
>    // <tgmath.h>
>    #undef cbrt
>    #define cbrt(x) _Generic((x),          \
>        float:   cbrtf(x),  /* this */     \
>        default: __constrndfunc(cbrt, (x)) \
>        )
> 
> The effective difference between my version above and Jim’s here is that
> if the user undefines `cbrtf`, suppressing that macro expansion,
> `cbrt(some_double)` will round according to the current constant
> rounding mode while `cbrt(some_float)` will round according to default
> rules.
> 
> I’ve quoted Jim’s letter below in full so we can discuss whether his
> reading of the CPF spec is correct, and which version is actually desired.
> 
> —Joel
> 
>> The current CFP spec (11) has:
>> 
>> ----------
>> Within the scope of an *FENV_ROUND *directive establishing a mode other
>> than *FE_DYNAMIC, *all floating-point operators and invocations of
>> functions indicated in Table 2 below,for which macro replacement has not
>> been suppressed (7.1.4), shall be evaluated according to the specified
>> constant rounding mode (as though no constant mode was specified and the
>> corresponding dynamic rounding mode had been established by a call to
>> *fesetround*). Invocations of functions for which macro replacement has
>> been suppressed and invocations of functions other than those indicated
>> in Table 2 shall not be affected by constant rounding modes — they are
>> affected by (and affect) only the dynamic mode. Floating constants
>> (6.4.4.2) that occur in the scope of a constant rounding mode shall be
>> interpreted according to that mode.
>> ----------
>> 
>> and (16) adds the following to 7.25:
>> 
>> ----------
>> A type-generic macro corresponding to a function indicated in Table 2 is
>> affected by constant rounding modes (7.6.2).
>> ----------
>> 
>> This implies that functions invoked by <tgmath.h> macros are affected
>> unless macro replacement has been suppressed. Thus in
>> 
>> #define STDC_WANT_IEC_60559_BFP
>> #include <tgmath.h>
>> #include <fenv.h>
>> double d;
>> float f;
>>>> {
>> #pragma STDC FENV_ROUND FE_UPWARD
>>>> // Case 1
>> … cbrt(d) …
>>>> // Case 2
>> … cbrt(f) …
>>>> // Case 3
>> #undef cbrtf
>> … cbrt(f) …
>>>> // Case 4
>> #undef cbrt
>> … cbrt(d) …
>>>> // Case 5
>> … cbft(f) …
>>>> }
>> 
>> the current CFP spec says:
>> 
>> Case 1 - which invokes function cbrt shall be affected by pragma,
>> because user has not suppressed macro expansion for cbrt. Note that
>> <tgmath.h> may itself undef cbrt, so must take care to assure the call
>> is affected
>> 
>> Case 2 - which invokes function cbrtf shall be affected by pragma,
>> because user has not suppressed macro expansion for cbftf
>> 
>> Case 3 - which invokes function cbrtf shall not be affected by pragma,
>> because user has suppressed macro expansion for cbrtf
>> 
>> Case 4 - which invokes cbrt (without help from suppressed <tgmath.h>
>> macro) shall not be affected by pragma, because user suppressed macro
>> expansion for cbrt
>> 
>> Case 5 - same as Case 4 but with argument converted to double
>> 
>> How does this requirement fit with C11? C11 leaves it unspecified
>> whether functions invoked by <tgmath.h> macros are subject to macro
>> replacement. The example in C11 6.5.1.1 provides a sample implementation
>> for the <tgmath.h> cbrt macro using the _Generic macro. This
>> implementation disables macro expansion for all the functions it
>> invokes. Thus it does not give the CFP-specified behavior above in Cases
>> 1 and 2. However, C11 does not say or imply that this sample
>> implementation is definitive. 
>> 
>> The following modification of the sample implementation in 6.5.1.1 would
>> give the CFP-specified behavior:
>> 
>>      #define cbrt(X) _Generic((X),                   \
>>                                  long double: cbrtl(X),     \
>>                                  default: __round_wise_cbrt(X),          \
>>                                  float: cbrtf(X)
>> 
>> where __round_wise_cbrt is affected by the constant rounding mode pragma
>> (like cbrt where <math.h> but not <tgmath.h> has been included).
>> 
>> Any concerns about what we've specified? Is the behavior good for users?
>> Is it reasonable to implement? Does it fit well enough with C11?
>> 
>> -Jim
> _______________________________________________
> Cfp-interest mailing list
> Cfp-interest at oakapple.net
> http://mailman.oakapple.net/mailman/listinfo/cfp-interest




More information about the Cfp-interest mailing list