问题
Floating point expressions can sometimes be contracted on the processing hardware, e.g. using fused multiply-and-add as a single hardware operation.
Apparently, using these this isn't merely an implementation detail but governed by programming language specification. Specifically, the C89 standard does not allow such contractions, while in C99 they are allowed provided that some macro is defined. See details in this SO answer.
But what about C++? Are floating-point contractions not allowed? Allowed in some standards? Allowed universally?
回答1:
Summary
Contractions are permitted, but a facility is provided for the user to disable them. Unclear language in the standard clouds the issue of whether disabling them will provide desired results.
I investigated this in the official C++ 2003 standard and the 2017 n4659 draft. C++ citations are from 2003 unless otherwise indicated.
Extra Precision and Range
The text “contract” does not appear in either document. However, clause 5 Expressions [expr] paragraph 10 (same text in 2017’s 8 [expr] 13) says:
The values of the floating operands and the results of floating expressions may be represented in greater precision and range than that required by the type; the types are not changed thereby.
I would prefer this statement explicitly stated whether this extra precision and range could be used freely (the implementation may use it in some expressions, including subexpressions, while not using it in others) or had to be used uniformly (if the implementation uses extra precision, it must use it in every floating-point expression) or according to some other rules (such as it may use one precision for float
, another for double
).
If we interpret it permissively, it means that, in a*b+c
, a*b
could be evaluated with infinite precision and range, and then the addition could be evaluated with whatever precision and range is normal for the implementation. This is mathematically equivalent to contraction, as it has the same result as evaluating a*b+c
with a fused multiply-add instruction.
Hence, with this interpretation, implementations may contract expressions.
Contractions Inherited From C
17.4.1.2 [lib.headers] 3 (similar text in 2017’s 20.5.1.2 [headers] 3) says:
The facilities of the Standard C Library are provided in 18 additional headers, as shown in Table 12…
Table 12 includes <cmath>
, and paragraph 4 indicates this corresponds to math.h
. Technically, the C++ 2003 standard refers to the C 1990 standard, but I do not have it in electronic form and do not know where my paper copy is, so I will use the C 2011 standard (but unofficial draft N1570), which the C++ 2017 draft refers to.
The C standard defines, in <math.h>
, a pragma FP_CONTRACT
:
#pragma STDC FP_CONTRACT on-off-switch
where on-off-switch is on
to allow contraction of expressions or off
to disallow them. It also says the default state for the pragma is implementation-defined.
The C++ standard does not define “facility” or “facilities.” A dictionary definition of “facility” is “a place, amenity, or piece of equipment provided for a particular purpose” (New Oxford American Dictionary, Apple Dictionary application version 2.2.2 (203)). An amenity is “a desirable or useful feature or facility of a building or place.” A pragma is a useful feature provided for a particular purpose, so it seems to be a facility, so it is included in <cmath>
.
Hence, using this pragma should permit or disallow contractions.
Conclusions
Contractions are permitted when
FP_CONTRACT
is on, and it may be on by default.The text of 8 [expr] 13 can be interpreted to effectively allow contractions even if
FP_CONTRACT
is off but is insufficiently clear for definitive interpretation.
回答2:
Yes, it is allowed.
For example in Visual Studio Compiler, by default, fp_contract
is on. This tells the compiler to use floating-point contraction instructions where possible. Set fp_contract
to off
to preserve individual floating-point instructions.
// pragma_directive_fp_contract.cpp
// on x86 and x64 compile with: /O2 /fp:fast /arch:AVX2
// other platforms compile with: /O2
#include <stdio.h>
// remove the following line to enable FP contractions
#pragma fp_contract (off)
int main() {
double z, b, t;
for (int i = 0; i < 10; i++) {
b = i * 5.5;
t = i * 56.025;
z = t * i + b;
printf("out = %.15e\n", z);
}
}
Detailed information about Specify Floating-Point Behavior.
Using the GNU Compiler Collection (GCC):
The default state for the FP_CONTRACT
pragma (C99 and C11 7.12.2).
This pragma is not implemented. Expressions are currently only contracted if -ffp-contract=fast
, -funsafe-math-optimizations
or -ffast-math
are used.
来源:https://stackoverflow.com/questions/49278125/is-floating-point-expression-contraction-allowed-in-c