constexpr and floating point rounding behaviour
Tags: programming
One of the new features in C++11 is the ability to declare variables (or even functions) to be a constant expression that can be evaluated at compile time. Not only does this make the code easier to read, it also offers increased performance while still being very maintainable.
I was thus very surprised to find out that, according to one of our
group members, constexpr
stopped working—the code had apparently
compiled on a personal machine but it would not compile in our
production environment at university. We used the following test program
to trace down the culprit:
#include <cmath>
int main(int, char**)
{
constexpr double a = 2.0;
constexpr double b = std::sqrt(a);
return 0;
}
And indeed, using our CMake
build environment, gcc would prove
unwilling to compile the code, responding with a very terse error
message:
constexpr.cc: In function ‘int main(int, char**)’:
constexpr.cc:6:35: error: ‘sqrt(2.0e+0)’ is not a constant expression
constexpr double b = std::sqrt(a);
After the initial bafflement about this seemingly incorrect compiler
error message, I happened to take a closer look at the compiler flags of
our build environment. The culprit then turned out to be
-frounding-math
. This flag instructs the compiler that floating point
rounding behaviour might change at runtime, so naturally, an
expression such as std::sqrt(2.0)
is not a constant expression any
more. Thus, I learned that:
$ g++ -std=c++11 constexpr.cc
$ ./a.out # Yay
$ g++ -std=c++11 -frounding-math constexpr.cc
constexpr.cc: In function ‘int main(int, char**)’:
constexpr.cc:6:35: error: ‘sqrt(2.0e+0)’ is not a constant expression
constexpr double b = std::sqrt(a);
Having lost a sizeable chunk of my daily sanity on this bug, I was of
course interested in tracing done its cause. The culprit turned out to
be CGAL. For reasons that are not completely clear
to my addled mind, the CGAL module for CMake
decides to set the
CXX_COMPILE_FLAGS
globally, regardless of whether a file actually
uses CGAL or not. The personal computer where the code was tested first
did not have a working installation of CGAL, so of course the code
compiled correctly.
Lessons learned: Check the compile flags and be wary around CMake
modules (though I have to point out that it is the implementation of
the module that caused the problems, not CMake
per se; quite the
opposite—I am a very fond user of CMake
).