cc99c11

If pragma STDC FENV_ACCESS is absent, does it mean default rounding mode?


I have problem with interpretation of C standard, the latest draft taken from http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2454.pdf.

Standard evaluation

The standard defines pragma STD FENV_ACCESS and states (7.6.1p2):

The FENV_ACCESS pragma provides a means to inform the implementation when a program might
access the floating-point environment to test floating-point status flags or run under non-default
floating-point control modes.

It is not clear why this pragma in necessary to run under non-default floating-point control modes. Is it because

Later in this paragraph of the standard we find:

If part of a program tests floating-point status flags or establishes non-default floating-point
mode settings using any means other than the FENV_ROUND pragmas, but was translated with the
state for the FENV_ACCESS pragma "off", the behavior is undefined.

It looks like testing current mode without changing it is not an undefined behavior. But the footnote in the same paragraph states:

In general, if the state of FENV_ACCESS is "off", the translator can assume that the flags are
not tested, and that default modes are in effect, except where specified otherwise by an
FENV_ROUND pragma.

The question

So if no pragma FENV_ACCESS was specified, does it mean default rounding mode is in effect?

Let's suppose that pragma FENV_ROUND is absent as well, and compiler assumes FENV_ACCESS is off by default, it is necessary for backward compatibility.

Example

Consider the following source code:

#include <math.h>
float func_01(float x) {
    return nearbyint(x);
}

The function nearbyint is described (7.12.9.3) as making rounding using current rounding mode. But the code does not have pragma FENV_ACCESS. Does it mean that current rounding mode may be ignored and nearbyint is same as roundeven?


Solution

  • C Draft n2454

    I wrote this answer based on the current C standard, 2018, but the question asks about a draft for a forthcoming standard. Upon reviewing the draft, there are significant changes, and this answer is not applicable.

    Notably, draft n2454 states in 7.6.1 2:

    … If part of a program tests floating-point status flags or establishes non-default floating-point mode settings using any means other than the FENV_ROUND pragmas, but was translated with the state for the FENV_ACCESS pragma "off", the behavior is undefined…

    Notably missing from this is this text from C 2018 that appeared just after “non-default floating-point mode settings”:

    … or runs under non- default mode settings,…

    The C 2018 text means that if code compiled with FENV_ACCESS on sets a non-default mode and sets code compiled with FENV_ACCESS off, then the behavior is not defined, merely because code compiled with FENV_ACCESS off is running in a non-default mode. The draft text does not contain this, which seems to imply that a caller can change the mode and call code compiled with FENV_ACCESS off, and the behavior should be defined. That means code compiled with FENV_ACCESS off must be prepared to run in any floating-point mode.

    The same paragraph in the draft also contains this new text:

    (When execution passes from a part of the program translated with FENV_ACCESS "off" to a part translated with FENV_ACCESS "on", the state of the floating-point status flags is unspecified and the floating-point control modes have their default settings.)

    Consider what happens when routine A with FENV_ACCESS on calls routine B with FENV_ACCESS off. When B returns, control passes from an access-off part of the program to an access-on part of the program. The sentence above says the floating-point control modes are then in their default settings. In other words, returning from an access-off routine must change the floating-point mode to the default. That seems odd. So I am not prepared to update this answer to cover the draft well.

    Answer for C 2018

    It is not clear why this pragma in necessary to run under non-default floating-point control modes.

    It is because it may be (depending on the C implementation) that the code the compiler generates must be different if floating-point operations are not known to be in the default mode. For example, when compiling code with FENV_ACCESS set to off, the compiler can compile a call to sin as a call to a fast version that assumes default rounding. But if FENV_ACCESS is set to on, it would compile the call to a slower version that tests the rounding mode and uses a corresponding implementation of the sine function.

    Because the code that must be generated is different for the on and off versions, the compiler must know whether FENV_ACCESS is on or off.

    So if no pragma FENV_ACCESS was specified, does it mean default rounding mode is in effect?

    No. If the FENV_ACCESS pragma is not present, the compiler is in its default state, which may be on or off, and that is implementation-defined.

    If the default is off and there is no pragma, then, yes, the default rounding mode should be in effect, meaning that, if you have designed your program correctly, then any code compiled with no FENV_ACCESS pragma is never executed in a non-default rounding mode. That is up to the program designer(s) to ensure.

    The function nearbyint is described (7.12.9.3) as making rounding using current rounding mode. But the code does not have pragma FENV_ACCESS. Does it mean that current rounding mode may be ignored and nearbyint is same as roundeven?

    If code with FENV_ACCESS set to off (either by default or explicitly) calls nearbyint, then the compiler can assume the default rounding mode is in effect, and it can call the fast version of nearbyint that itself assumes the default rounding mode.

    Note that round-to-nearest-ties-to-even is overwhelming the default rounding mode, but this is not specified by the C standard unless Annex F is in effect.