“Please write a C++ function that takes a circle’s diameter as a float and returns the circumference as a float.”

It sounds like the sort of question you might get in the first week of a C++ programming class. And yet. This question is filled with subtlety if you dig into it. Let’s try some solutions.

Updated June 27 to add a link to a sample program, and instructions on how to bloat the executable that it creates by 20 KiB. Updated July 2 to add ‘Take seven’. Updated Jan 5, 2015 to add VC++ section at the end.

Student: How about this?

#include <math.h>

float CalcCircumference1(float d)

{

return d * M_PI;

}

Teacher: This code may compile. But it may not. For M_PI is not part of the C or C++ standards. If you compile with VC++ 2005 then this will work, but with later versions you need to #define _USE_MATH_DEFINES prior to including math.h in order to request these non-standard constants. And, having done that you will have written code which may well not compile on other compilers.

Take two

Student: Thank you for your wisdom teacher. I have removed the dependency on the non-standard M_PI constant. How about this?

float CalcCircumference2(float d)

{

return d * 3.14159265358979323846;

}

Teacher: That’s better. This code will compile and will work as intended. But it is inefficient. You are multiplying a float by a double-precision constant. The compiler will have to convert the float input to a double, and then convert the result from a double to a float. If you compile for SSE2 then this adds two instructions to the dependency chain and may triple the latency! In many contexts this extra cost will not matter, but in an inner loop it can be quite significant.

If you compile for x87 then the conversion to double is free but the conversion to float is expensive – so expensive that some optimizers may omit the conversion to double which can lead to surprising results, such as CalcCircumference(r) == CalcCircumference(r) returning false!

Take three

Student: Thank you for wisdom teacher. I don’t know what SSE2 or x87 are but I see the elegance and poetry of keeping my types consistent. I will use a float constant. How about this, says the student?

float CalcCircumference3(float d)

{

return d * 3.14159265358979323846f;

}

Teacher: Ah, well done. That ‘f’ at the end of the constant makes all the difference. If you were to look at the generated machine code you would see that it is leaner and cleaner. However I have stylistic objections. Does it not seem sloppy to have this cryptic constant embedded in the function? Even though ‘pi’ is unlikely to change it would be cleaner and less error prone to give it a name and put it in a header file.

Take four

Student: Thank you teacher. This wisdom is much easier to understand. I will put the line of code below in a shared header file, and use it in my function. How about this, says the student?

const float pi = 3.14159265358979323846f;

Teacher: Well done. By using the ‘const’ keyword you have both indicated that the variable cannot and should not be modified, and you have allowed it to be placed in a header file. But, I’m afraid we must now delve into some subtleties of the C++ scope rules.

By marking ‘pi’ as const you are also implicitly marking it as static. This is fine for integral types, but for non-integral types (float, double, array, class, struct) there may be storage allocated for this variable, potentially in every translation unit that includes your header file. In come cases you may end up with dozens or even hundreds of copies of the float, thus bloating your executable.

Take five

Student: Are you frickin’ kidding me? Now what?

Teacher: Yeah, it’s a bit of a mess. You could tag your variable with __declspec(selectany) or __attribute__(weak) in order to tell VC++ and gcc respectively that it is okay to just retain one of the many copies of the constant. But since we are in the idealistic world of academia right now I’m going to insist that you stick to standard C++ constructs.

Take six

Student: You mean like this? Using the C++11 constexpr?

constexpr float pi = 3.14159265358979323846f;

Teacher: Yes. Your code is now perfect. Of course it won’t compile with VS 2013 because that compiler doesn’t support constexpr. But you can always use the Visual C++ Compiler Nov 2013 CTP toolset, or wait for Dev 14. Or use recent versions of gcc or clang.

Student: Can I use a #define?

Teacher: No!

Student: Screw this. I’m quitting school to become a barista.

Take seven (newly added July 2, 2014)

Student: Wait, I just remembered. This is easy. I just have to do this:

mymath.h:

extern const float pi; mymath.cpp:

extern const float pi = 3.14159265358979323846f;

Teacher: Indeed that is the correct solution in many cases. But what if you are building a DLL, and mymath.h is included by functions outside of that DLL? Now you have to deal with the complexity and cost of exporting and importing this symbol.

Ultimately the problem is the confusion caused by the rules being totally different for integral types. It is appropriate and recommended to put this in a C++ header file:

const int pi_i = 3;

It’s not a very accurate version of pi, but the point is that integral constants in header files don’t allocate storage, whereas non-integral constants do. This distinction is poorly understood, and occasionally important.

I learned the implied ‘static’ in ‘const’ a few years ago when I was asked to investigate why one of our key DLLs had suddenly gotten 2 MB larger. It turns out there was a const array in a header file and we had thirty copies of it in the DLL. So sometimes it does matter.

And yes, I still think that using a #define is a terrible solution. It may be the least-worst solution, but that makes me unhappy. I once dealt with compile errors caused by a #define of ‘pi’ and they did not make me happy. Namespace pollution is the main reason why #define should be avoided as much as possible.

Conclusion

I’m not sure what the lesson is here. The problems of putting const float (or const double, or const structures or arrays) in header files are not well understood. Most large programs have duplicate static const variables because of this, and sometimes they are of non-trivial size. I think that constexpr solves this but I haven’t used it enough to be certain.

I have seen programs waste hundreds of KB because of a const array defined in a header file. I have also seen a program that ended up with 50 copies of a class object (plus 50 constructors and destructors) because it was defined as const in a header file. Something to be aware of.

You can see that this happens with gcc by downloading a test program here. Build it with make and then run “objdump -d constfloat | grep flds” to see the four loads from four adjacent data segment addresses. FWIW. If you want to waste more space then add this to header.h:

const float sinTable[1024] = { 0.0, 0.1, };

With gcc this will waste 4 KiB per translation unit (source file) for a total of 20 KiB of bloat in the final executable, even though the table is never referenced.

As usual, floating-point math is full of complexities, although in this case I think the C++ language’s slow evolution is more to blame.

Some more reading:

http://stackoverflow.com/questions/3709207/c-semantics-of-static-const-vs-const

VC++ – avoiding duplicates, and unavoidable duplicates

The /Gw compiler option that was introduced in VC++ 2013 Update 2 puts each global variable in its own ‘COMDAT’, which lets the linker discard duplicates. In some cases this avoids the costs of having const/static globals declared in header files. This switch saved about 600 KB in Chrome – see this change for details. Some of the savings was (surprise!) from removing thousands of copies of twoPiDouble and piDouble (and twoPiFloat and piFloat).

However the VC++ 2013 STL has several static or const inline objects that /Gw is unable to discard. These are all one-byte objects but in Chrome they add up to over 45 KB of waste! I filed a bug for this behavior and have been told that the issue is fixed in VC++ 2015.