Boost logo

Boost :

From: Daniel Frey (d.frey_at_[hidden])
Date: 2003-06-08 15:53:34


On Sun, 08 Jun 2003 16:56:53 +0200, Paul A Bristow wrote:

> You can seen an example of extending to a 'new' constant 'gamma' in the
> examples testFunctionConstants/gamma_function_constants.hpp.

Either I don't understand the example or we are talking past each other. I
don't meant to extend the framework with new constants, but with
definitions of existing constants for new types. Maybe the attached
example of a small experiment of mine clarifies what I was thinking about.
Look at the "Roguewave"-extension for pi. If I imagine to use
"boost/math_constants.hpp" in the company I work, it would be encapsulated
into another header "base/math_constants.h" and it would provide all
constants for Roguewawve's decimal type, too. I already used the
lambda-library and tought it about the decimal type, which worked very
smooth and I was really happy to see that the library designers didn't
limited it to build-in types. A key-feature for me!

> defining the constant as a 40 decimal digit string.

Roguewave's types can be used with precisions up to 1024 bits - probably
more. I don't think that a one-size-fits-all approach can work in the area
of numeric computations.

> If Boosters agree that this scheme is an acceptable way to go, the the
> example and guidance could be made more helpful to provide the
> encouragement you rightly say is needed.
>
> But first the overall strategy needs agreement.

Indeed. So if I still missed that the constants can be provided by the
user for their types, please let me know. Otherwise, we should find a
consensus whether such a feature is needed. I personally think it is, but
if the majority thinks it's not that important... :)

Note that the attached file is not meant to offend you in any way. It way
just a toy example and maybe you can take some ideas out of it. It's not
meant to replace your code as I think you have put a lot of ideas in it
and provide features I don't even know of :)

Regards, Daniel

---
#include <iostream>
using namespace std;
namespace math
{
    // Generic base class for all constants
    template< typename T, template< class > class F > struct constant
    {
        // A cast-to-anything-operator :)
        template< typename U > operator U() const { return F< U >()(); }
#define ADD_OPERATOR( OP ) \
        template< typename U > friend U operator OP( const T& lhs, const U& rhs ) \
        { U nrv( static_cast< U >( lhs ) ); nrv OP##= rhs; return nrv; } \
        template< typename U > friend U operator OP( const U& lhs, const T& rhs ) \
        { U nrv( lhs ); nrv OP##= static_cast< U >( rhs ); return nrv; }
        ADD_OPERATOR( + );
        ADD_OPERATOR( - );
        ADD_OPERATOR( * );
        ADD_OPERATOR( / );
#undef ADD_OPERATOR
    };
    // Here's the definition for pi for all types (can be extended by UDTs):
    template< typename T > struct pi_value;
    template<> struct pi_value< float > { float operator()() const { return 3.14; } };
    template<> struct pi_value< double > { double operator()() const { return 3.1416; } };
    template<> struct pi_value< long double > { long double operator()() const { return 3.1415927; } };
    /*
    // For expensive constructions, maybe this is an option:
    template<> struct pi_value< RWDecimal< RWMP3Int > > {
        const RWDecimal< RWMP3Int >& operator()() const {
            static const RWDecimal< RWMP3Int > value( "3.1415926535897932384626433832" );
            return value;
        }
    };
    */
    // Here's the single line to create a useful interface
    struct pi_t : constant< pi_t, pi_value > {} pi;
    // Another one:
    template< typename T > struct pi2_value;
    template<> struct pi2_value< float > { float operator()() const { return 9.87; } };
    template<> struct pi2_value< double > { double operator()() const { return 9.8696; } };
    template<> struct pi2_value< long double > { long double operator()() const { return 9.8696044; } };
    struct pi2_t : constant< pi2_t, pi2_value > {} pi2;
    // And their relationship:
    pi2_t operator*( const pi_t&, const pi_t& ) { return pi2_t(); }
    // Some obvious (?) constants:
#define CONSTANT_VALUE( name, value ) \
    template< typename T > struct name##_value { T operator()() const { return value; } }; \
    struct name##_t : constant< name##_t, name##_value > {} name;
    CONSTANT_VALUE( minus_one, -1 );
    CONSTANT_VALUE( zero, 0 );
    CONSTANT_VALUE( one, 1 );
    CONSTANT_VALUE( two, 2 );
    CONSTANT_VALUE( three, 3 );
    CONSTANT_VALUE( ten, 10 );
#undef CONSTANT_VALUE
    minus_one_t operator-( const one_t& ) { return minus_one_t(); }
    one_t operator-( const minus_one_t& ) { return one_t(); }
    zero_t operator-( const zero_t& ) { return zero_t(); }
    // Another one:
    template< typename T > struct half_pi_value;
    template<> struct half_pi_value< float > { float operator()() const { return 1.57; } };
    template<> struct half_pi_value< double > { double operator()() const { return 1.5708; } };
    template<> struct half_pi_value< long double > { long double operator()() const { return 1.5707963; } };
    struct half_pi_t : constant< half_pi_t, half_pi_value > {} half_pi;
    // And their relationship:
    // The user can now write 'pi / two' and needn't remember
    // whether it was 'half_pi' or 'pi_by_2' or whatever...
    half_pi_t operator/( const pi_t&, const two_t& ) { return half_pi_t(); }
    // An example for another optimization type
    // to solve the naming dilemma, this time providing 'sin( pi )'
    template< typename T > T sin( const T& v ) { return std::sin( v ); }
    zero_t sin( const pi_t& ) { return zero_t(); }
    one_t sin( const half_pi_t& ) { return one_t(); }
}
int main()
{
    // Usage example:
    using namespace math;
    const float f = 1.;
    const double d = 1.;
    const long double ld = 1.;
    cout << pi * f * f << endl;
    cout << pi * d * d << endl;
    cout << pi * ld * ld << endl;
    cout << static_cast< double >( pi ) << endl;
    cout << static_cast< float >( pi ) * d * d << endl;
    cout << pi * pi * f << endl;
    cout << pi * pi * d << endl;
    cout << pi * pi * ld << endl;
    cout << d * -sin( pi / two ) << endl;
    cout << d * -sin( pi / 2. ) << endl;
    long double r = 0;
    for( int i = 0; i < 10000000; ++i )
        r = d * -sin( pi / 2. ); // try replacing the "2." by "two" - makes this test program 100x faster! 
    cout << r << endl;
}

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