The implementation of pragma FENV_ROUND requires some support by the library. This RFC presents a possible way, how this support could be implemented.
Background
The latest C2x C standard draft introduces a pragma FENV_ROUND (https://www.iso-9899.info/n3047.html#7.6.2), which defines constant rounding mode in addition to dynamic, which is set by fesetround
. These modes have natural representation on platforms that support static rounding, like RISC-V: if the mode is taken from an instruction field, it is constant mode, if from a register, it is dynamic. On other platforms, constant rounding can be implemented using dynamic rounding (7.6.2p5).
The Standard requires that the functions called in the scope of pragma FENV_ROUND be evaluated using dynamic rounding mode with some exceptions (7.6.2p4):
Within the scope of a FENV_ROUND pragma establishing a mode other than FE_DYNAMIC … invocations of functions indicated in the table 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 the table below shall not be affected by constant rounding modes – they are affected by (and affect) only the dynamic mode.
That is, the same function may be evaluated using different rounding modes in the scope of pragma FENV_ROUND:
#pragma STDC FENV_ROUND FE_TOWARDZERO
y = sin(x); // sin is evaluated using rounding to zero
z = (sin)(x); // sin is evaluated using dynamic rounding
The problem is how to implement such function calls.
Solution
The compiler could recognize if a called function requires constant rounding mode and replace it with some other code, for example with a call to a different function, like sin
→ sin_rtz
. This way however looks inflexible. The compiler must do this transformation for all the functions mentioned in the Standard, no matter, if they are implemented or not. The library must know how the compiler transforms the functions and provide the necessary functions or macros for constant modes (like sin_rtz
). Implementations of the compiler and the library become tightly coupled. In particular, if the set of these functions is extended (by using a new floating type, for example), the compiler would require changes.
The wording used in the Standard (“for which macro replacement has not been suppressed”) may be understood as a hint at using a preprocessor for this feature. The library could define macros for functions like sin
to get implementation conformant to the Standard. For example, sin
for FE_TOWARDZERO could be transformed to sin_rtz
, which could be implemented as follows:
static inline double sin_rtz(double x) {
int saved_mode = fegetround();
fesetround(FE_TOWARDZERO);
double res = (sin)(x);
fesetround(saved_mode);
return res;
}
Such transformation may be performed using a macro that represents the constant rounding mode set by pragma FENV_ROUND.
Proposal
Let’s introduce a macro __ROUNDING_MODE__
, which expands to an identifier that reflects constant rounding mode For example, OpenCL modifiers (The OpenCL™ C Specification) may be used with addition of a value for FE_TONEARESTFROMZERO:
FE_TOWARDZERO _rtz
FE_TONEAREST _rte
FE_UPWARD _rtp
FE_DOWNWARD _rtn
FE_TONEARESTFROMZERO _rta
FE_DYNAMIC empty
Using such macro, the special treatment of the functions mentioned in the Standard can be implemented with the simple macros:
#define CONCAT(a, b) CONCAT_(a, b)
#define CONCAT_(a, b) a##b
#define ADD_ROUNDING_MODE_SUFFIX(func) CONCAT(func, __ROUNDING_MODE__)
#define sin(x) ADD_ROUNDING_MODE_SUFFIX(sin)(x)
In this case the call of sin
in the code:
#pragma STDC FENV_ROUND FE_TOWARDZERO
y = sin(x);
would be expanded to y = sin_rtz(x)
, which could have the implementation as inline function presented above or be implemented as a library function or be an another macro etc.
In this case the library provides the set of macro definition for the supported functions. The implementation may be any, the library support the only restriction is using values of __ROUNDING_MODE__
as a part of implementing functions or macros.
Any feedback is appreciated.