Boost logo

Boost :

From: Ed Brey (edbrey_at_[hidden])
Date: 2001-10-25 14:10:14


From: "Greg Colvin" <gcolvin_at_[hidden]>
> >
> > But alas, I can't make it work. The compiler complains about multiple functions named "constant" that differ only by return type.
>
> EDG compiles this fine in --strict mode. I've attached my test program.

I was trusting my compilers, which of course is not always safe. Neither VC6 nor g++ 2.95.3 like the code. Looking at the code, I could see what their beef was, but looking more closely I see that the duplication among function names and arguments should not be a problem here.

> > There is also the same constructor issue that Michael's pattern has. Here's the summary:
> > - With the constructor, VC generates code for each constant, whether referenced or not.
>
> I'd hate to give up a good interface just because some systems generate
> uneccessarily bad code. I expect that on most systems we can tweak
> the implementation to solve such problems.

I agree. Fortunately, this can be solved with the macro hacks that I hand waved about before. However, none of this may be an issue if there is an equal or better interface that avoids the need for a non-builtin variable definition.

> > Of course, there is still the option of simply providing a bunch of simple double constants for non-generic uses, and then a
> separate simple function interface for generic uses, like this
>
> I'd prefer long double constants.

I was thinking double in response to Peter Dimov's point about literals defaulting to double. Pi would do so for consistency. One argument I can see to the contrary is that with literals, you often don't have enough digits to matter, and if you do it's quite obvious that you have a whole lot of digits. Pi would silently lose precision when long double is used.

However, besides consistency, a long double Pi has the problem that it will trigger loss-of-precision warnings when used in the common case manner (if the platform has different representations for double and long double). This seems unacceptable.

I wish I had a great answer. The closest I can think of is to put double, long double, and float constants each in its own namespaces, although that has its own problems. On the other hand, it also solves a problem that hasn't been explicitly presented yet:

Suppose someone wrote a non-generic program, but used typedefs because he wasn't sure of the precision he'd need. For example:

typedef double Real;
using namespace boost::math;
Real foo = 4./3. * pi * r * r * r; // and a bunch of other neat math stuff

This works fine for a while. Then the user finds a case where he'd prefer to trade off some speed for accuracy, he might want to change the first two lines to work with long doubles instead of doubles. His literals are small, and so don't have any value to be long double, but that isn't the case for pi. Having to change this to pi_l would be undesirable.

But wait. The literals really do need to change, since unsigned long(4./3.) is not the same as 4.L/3.L. So you really do need to write the entire program with a given precision in mind, or wrap everything in casts like "Real(4)". But if one is going to do all that, wrapping pi as "Real(pi_l)" isn't much by comparison. And this would leave plain old pi available as a warning-free double.

> Of course Beman says "Please do not make any design decisions for math
> constants based on brain-dead compilers."

Agreed. Of course, the same techniques that break compilers also tend to fight the language. Using the operator double() approach, gone is the obviousness of parentheses meaning function and no parentheses meaning variable. Instead, what looks like a variable actually has the restrictions of a function. This may be one of those cases when an exception to the norm is a good thing, but such exceptions need to be examined closely.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk