Boost logo

Boost :

From: Daryle Walker (darylew_at_[hidden])
Date: 2000-07-14 16:58:33


on 7/13/00 6:51 AM, Paul A. Bristow at pbristow_at_[hidden] wrote:

[SNIP]
>>> typedef double real_type; // float or double or long double.
> [PAB] Indeed ugly, but your suggested:
>
> #include <iostream>
>
> namespace boost
> {
> template<class T>
> struct mathconst
> {
> static const double pi;
> // ... etc
> };
> }

Shouldn't the value type be 'T' and not hard-wired 'double'?

I think there may be problems using floating-point static constant members.
The standard's std::numeric_limits template class uses (inline) functions
for stuff returning floating-point values. You could do the same. Using
functions also lets user-defined classes set up math constants. It also
lets some types work with the direct literal method you prefer and others
with computing your constant (for types that can produce accurate-enough
representations through computation alone).

//==========================================================================
namespace boost
{

template <typename T>
struct math_const
{
    // a specific instantiation should override this to 'true'
    static bool is_specialized = false;

    // uninteresting defaults should follow, unless overridden

    static T zero() { return T(); }
    static T one() { T t = zero(); return ++t; }
    static T two() { T t = one(); return ++t; }
    static T one_half() { return one() / two(); }

    static T sqrt_two();
    static T one_div_sqrt_two() { return sqrt_two() / two(); }

    static T pi(); // Archimedes constant
    static T e(); // natural logarithm base
    static T gamma(); // Euler constant
    static T phi(); // golden ratio

    static T ln_2();
    static T ln_10();

    static T radian_per_degree() { return pi() / static_cast<T>(180); }
    static T degree_per_radian() { return one() / radian_per_degree(); }

    static T sin_one();
    static T cos_one();

    //...
};

// specializations follow
template <>
struct math_const<double>
{
    static bool is_specialized = true;

    // uninteresting defaults should follow, unless overridden

    static T zero() throw() { return 0.0; }
    static T one() throw() { return 1.0; }
    static T two() throw() { return 2.0; }
    static T one_half() throw() { return 0.5; }

    static T sqrt_two() throw()
        { return 1.41421356237309504880168872420969807857; }
    static T one_div_sqrt_two() throw()
        { return 0.7071067811865475244008443621048490392848; }

    static T pi() { return 3.141592653589793238462643383279502884197; }
    static T e() { return 2.718281828459045235360287471352662497757; }
    static T gamma() { return 0.5772156649015328606065120900824024310422; }
    static T phi() { return 1.61803398874989484820458683436563811772; }

    static T ln_2() { return ...; }
    static T ln_10() { return ...; }

    static T radian_per_degree() { return pi() / 180.0; }
    static T degree_per_radian() { return 1.0 / radian_per_degree(); }

    static T sin_one() throw()
        { return 0.84147098480789650665250232163029899962256306; }
    static T cos_one() throw()
        { return 0.54030230586813971740093660744297660; }

    //...
};

} // namespace boost
//==========================================================================

[SNIP]
> Also has some ugliness when the user needs it.
>
> const long double longPi = boost::mathconst<long double>::pi;
>
> [PAB] is pretty long winded. What do others think about this?

There's a tradeoff to using namespaces and/or templates.

> [PAB] In practice users will nearly always only use float, double, or long
> double.

But since we're using templates, we should be flexible enough to allow
appropriate user-types too.

-- 

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