[Cfp-interest] implementing tgmath for constant rounding modes

Joel C. Salomon joelcsalomon at gmail.com
Thu Jun 7 17:43:58 PDT 2012


On 06/07/2012 03:48 PM, Fred J. Tydeman wrote:
>
> On Sat, 2 Jun 2012 08:22:55 -0700 Jim Thomas wrote:
>>
>> Take the example of cbrt. <math.h> could have
>>
>> #define cbrt(x) __roundwise_cbrt(x)
>>
>> Then the translator would have to keep track of whether a constant rounding mode is in effect at 
>> each call site for __roundwise_cbrt and, if so, what the rounding direction is. 
>
> So, in effect, __roundwise_cbrt becomes a special keyword/token/symbol to the compiler.
> And the same for all the other math functions (that is a lot of names to treat special).
<snip>
> Would it be better to add just one new attribute, eg,
>   #define cbrt(x) [[ROUNDWISE]] cbrt(x)
> that would be the same for all compilers?

A question about implementing this, at the library level: How is the
static mode being passed to the function we're calling?

This can be faked by setting the dynamic rounding mode to the
currently-selected static mode before the function call, but this adds
the overhead of setting & checking the dynamic mode. Is that at all
significant?

E.g., assume the following declarations of highly-efficient,
1ulp-accurate functions:

    double __cbrt_round_downward(double),
        __cbrt_round_tonearest(double),
        __cbrt_round_towardzero(double),
        __cbrt_round_upward(double);

The following then becomes possible:

    inline double cbrt(double x) {
        #pragma STDC FENV_ACCESS ON
        switch (fegetround()) {
        case FE_DOWNWARD:   __cbrt_rnd_downward(x); break;
        case FE_TONEAREST:  __cbrt_rnd_tonearest(x); break;
        case FE_TOWARDZERO: __cbrt_rnd_towardzero(x); break;
        case FE_UPWARD:     __cbrt_rnd_upward(x); break;
        }
    }

Apply the appropriate compiler magic:

    #define cbrt(x) [[ROUNDWISE]] cbrt(x)

and we then have this use:

    {
        #pragma STDC FENV_ROUND FE_DOWNWARD
        /* stuff */
        x = cbrt(8.0);
        /* more stuff */
    }

which the compiler turns into

    {
        #pragma STDC FENV_ROUND FE_DOWNWARD
        /* stuff */
        #pragma STDC FENV_ROUND FE_DYNAMIC
        fesetround(FE_DOWNWARD);
        x = cbrt(8.0);
        #pragma STDC FENV_ROUND FE_DOWNWARD
        /* more stuff */
    }

On a system optimized for static rounding modes, this seems to me
somewhat odd -- but it can work. (Or is it not odd at all?)

An alternative (which I proposed in my email of April 19, see
<http://mailman.oakapple.net/pipermail/cfp-interest/2012-April/000354.html>)
is a compiler intrinsic that behaves like fegetround(), but at compile
time; this requires less magic insertion of fesetround().

--Joel


More information about the Cfp-interest mailing list