
Boost : 
From: Paul A. Bristow (boost_at_[hidden])
Date: 20030610 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 128bit 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: boostbounces_at_[hidden]
 [mailto:boostbounces_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
 lambdalibrary 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 buildin types. A keyfeature 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 onesizefitsall 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 casttoanythingoperator :)
 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