|
Boost : |
From: Paul A. Bristow (boost_at_[hidden])
Date: 2003-06-10 03:11:29
Sorry I have misunderstood your wish - for existing constants in different types
rather than new constants in existing types.
Your example is interesting. I think that providing a Macro value allows this
sort of UDT extensions code (very like Michael Kenniston's examples).
My thesis that 40 decimal digits are enough is because it is enough for all
existing floating point hardware, up to 128 significands. I believe that anyone
wanting more is likely to be using a separate 'unlimited' precision package
anyway.
There is also an example of a UDT _interval_ - a 128-bit quad_float type, used
by Victor Shoup's NTL package. But it does require using the NTL generator
program to create the exactly representable values. (See test_quad_float.cpp
example).
I believe that interval constants are an important feature - and quite novel.
Paul
Paul A Bristow, Prizet Farmhouse, Kendal, Cumbria, LA8 8AB UK
+44 1539 561830 Mobile +44 7714 33 02 04
Mobile mailto:pabristow_at_[hidden]
mailto:pbristow_at_[hidden]
| -----Original Message-----
| From: boost-bounces_at_[hidden]
| [mailto:boost-bounces_at_[hidden]]On Behalf Of Daniel Frey
| Sent: Sunday, June 08, 2003 9:54 PM
| To: boost_at_[hidden]
| Subject: [boost] RE: RE: Math Constants Formal Review - is extensible.
|
|
| 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;
| }
|
| _______________________________________________
| Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
|
|
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk