2

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

  • setting these non-default modes require write to control mode register, or
  • the pragma is necessary always even if non-default current mode is already set?

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?

1 Answers1

1

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.

Community
  • 1
  • 1
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Thank you! It means by the way that description of `nearbyint`(and some other functions) is a bit misleading in the standard. – Serge Pavlov Mar 18 '20 at 05:10
  • @SergePavlov: In what way is it misleading? Calls to `nearbyint` behave as documented provided the program obeys the rules. If the program violates the rules, the routine might behave differently. That is true for all library routines and program behavior generally, not just floating-point. – Eric Postpischil Mar 18 '20 at 10:17
  • `nearbyint` behaves as documented only if `pragma FENV_ACCESS ON` is in effect. Otherwise compiler must replace it with `roundeven`. It it does not do that, library function, which knows nothing about pragmas, will do rounding according to the current rounding mode, which can differ from the default. Depending on optimization level the result will be different. This is undefined behavior. The standard should have stated that using `nearbyint` without `pragma FENV_ACCESS ON` is undefined behavior. – Serge Pavlov Mar 18 '20 at 11:24
  • Re “The standard should have stated that using `nearbyint` without pragma `FENV_ACCESS ON` is undefined behavior”: It should not say that because it is not true. It is true that using `nearbyint` without `FENV_ACCESS` set to “on” **and** running in non-default mode settings has undefined behavior, and the standard does say that. 7.6.1 2 says “If part of a program … runs under non-default mode settings, but was translated with the state for the FENV_ACCESS pragma "off", the behavior is undefined.” – Eric Postpischil Mar 18 '20 at 11:44
  • This is the key point. The excerpt from the standard you provided is from C11. But the draft of the forthcoming standard has different wording (n2454 cited in the question): "If part of a program tests floating-point status flags or establishes non-default floating-point mode settings ... ". There is no mention about "runs under non-default mode settings" anymore. So nothing in the new standard draft indicates that reading mode settings requires pragma FENV_ACCESS. – Serge Pavlov Mar 18 '20 at 16:36
  • @SergePavlov: Actually, my quote is from the C 2018 standard. For the draft you cite, it has this: “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.” That means they are changing out the floating-point environment works: It looks like if code compiled with “off” calls `nearbyint` or other routines that care about rounding, the called routine will operate with default rounding mode. – Eric Postpischil Mar 18 '20 at 17:19