[MPL] rational_c and fixed_c

A while back it was mentioned that boost::mpl::rational_c and boost::mpl::fixed_c needed redesigning. Before I attempt to tackle this task anymore than I already have, I should have some questions answered. 1. For those compilers for which BOOST_NO_LIMITS is defined, I've learned that there is no way to include a floating-point number in an integral constant expression. Does this mean that no matter how I define fixed_c::value, I cannot pass fixed_c as a template argument to a metafunction that ordinarily takes integral_c as input? Do I have to duplicate the functionality as a result? If so, what should be the naming convention for these new metafunctions? 2. Should I define fixed_c in terms of rational_c, to reduce the amount of functionality I may need to duplicate? Something like this: template <typename IntType, IntType N, IntType PowerOf10> struct fixed_c { typedef rational_c<IntType,...,...> type; }; 3. Eventually the compile-time rational-number representation will need to be converted to a runtime floating-point value. Should I define this conversion function within rational_c (which means an additional template parameter, with either function or class scope, defining the return value type), within a wrapper struct, or somewhere else? And how? 4. I only have to deal with rational numbers, right? I don't have to worry about accurately representing a transcendental number or a complex-number expression? TIA! Cromwell Enage __________________________________ Do you Yahoo!? Yahoo! Domains � Claim yours for only $14.70/year http://smallbusiness.promotions.yahoo.com/offer

Cromwell Enage <sponage@yahoo.com> writes:
A while back it was mentioned that boost::mpl::rational_c and boost::mpl::fixed_c needed redesigning.
Before I attempt to tackle this task anymore than I already have, I should have some questions answered.
1. For those compilers for which BOOST_NO_LIMITS is defined, I've learned that there is no way to include a floating-point number in an integral constant expression.
That's true of all C++ compilers, regardless of how BOOST_NO_LIMITS is set. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com

--- David Abrahams <dave@boost-consulting.com> wrote:
1. For those compilers for which BOOST_NO_LIMITS is defined, I've learned that there is no way to include a floating-point number in an integral constant expression.
That's true of all C++ compilers, regardless of how BOOST_NO_LIMITS is set.
Okay, my bad. But I still need those other questions answered. And if there are any other design issues I overlooked, please let me know as well. Cromwell Enage __________________________________ Do you Yahoo!? Yahoo! Domains � Claim yours for only $14.70/year http://smallbusiness.promotions.yahoo.com/offer

Cromwell Enage writes:
A while back it was mentioned that boost::mpl::rational_c and boost::mpl::fixed_c needed redesigning.
Before I attempt to tackle this task anymore than I already have, I should have some questions answered.
1. For those compilers for which BOOST_NO_LIMITS is defined, I've learned that there is no way to include a floating-point number in an integral constant expression.
As Dave already replied, BOOST_NO_LIMITS has no bearing on it; it's simply not allowed.
Does this mean that no matter how I define fixed_c::value, I cannot pass fixed_c as a template argument to a metafunction that ordinarily takes integral_c as input?
Yep, that's what it means.
Do I have to duplicate the functionality as a result?
Well, it's not really _duplicating_ of functionality; you have to implement the same set of primitives for a different representation, yes.
If so, what should be the naming convention for these new metafunctions?
I assume we are talking about arithmetic metafunctions in particular. Well, the ideal solution is outlined in http://article.gmane.org/gmane.comp.lib.boost.user/5668/, but for the purpose of implementing the functionality itself I suggest you abstract from all these complications and simply name these as you wish, e.g. 'fp_plus', 'fp_minus', etc., or anything along these lines.
2. Should I define fixed_c in terms of rational_c, to reduce the amount of functionality I may need to duplicate? Something like this:
template <typename IntType, IntType N, IntType PowerOf10> struct fixed_c { typedef rational_c<IntType,...,...> type; };
If it gives us a satisfactionary range of representable values, sure.
3. Eventually the compile-time rational-number representation will need to be converted to a runtime floating-point value. Should I define this conversion function within rational_c (which means an additional template parameter, with either function or class scope, defining the return value type), within a wrapper struct, or somewhere else? And how?
Within the wrapper struct, in form of a conversion operator, so we can write something like typedef float_<2,718281828> e; std::cout << e;
4. I only have to deal with rational numbers, right? I don't have to worry about accurately representing a transcendental number or a complex-number expression?
As long as rational number representation gives us enough domain space, sure. -- Aleksey Gurtovoy MetaCommunications Engineering

I now have a working implementation of fraction_c and rational_c, as well as arithmetic metafunctions that operate on them, at <http://groups.yahoo.com/group/boost/files/mpl_math.zip> (tested on MinGW 3.2 and MSVC 7.1). --- Aleksey Gurtovoy <agurtovoy@meta-comm.com> wrote:
Well, the ideal solution is outlined in
http://article.gmane.org/gmane.comp.lib.boost.user/5668/,
but for the purpose of implementing the functionality itself I suggest you abstract from all these complications and simply name these as you wish, e.g. 'fp_plus', 'fp_minus', etc., or anything along these lines.
Yeah, I'll hold off on implicit rational-to-fixed conversions et. al. for a while.
2. Should I define fixed_c in terms of rational_c, to reduce the amount of functionality I may need to duplicate? Something like this:
template <typename IntType, IntType N, IntType PowerOf10> struct fixed_c { typedef rational_c<IntType,...,...> type; };
If it gives us a satisfactionary range of representable values, sure.
Hmmm, maybe it won't. A decimal of sufficient size may be out of the value range that a rational (which I've actually implemented as a fraction) may represent; likewise, no decimal in mathematical existence can exactly represent, say, one-third.
3. Eventually the compile-time rational-number representation will need to be converted to a runtime floating-point value. Should I define this conversion function within rational_c (which means an additional template parameter, with either function or class scope, defining the return value type), within a wrapper struct, or somewhere else? And how?
Within the wrapper struct, in form of a conversion operator, so we can write something like
typedef float_<2,718281828> e; std::cout << e;
Ah, haven't done that yet. What I've done for the moment was something like this: typedef rational_c<int,4,5,8> shoe_size; typedef rational_wrapper<float,shoe_size> shoe_wrap; std::cout << shoe_wrap::value(); That's what I actually meant by wrapper struct, but if you want me to use a default return type, I should be able to reduce the code down to your type of syntax. My current focus with regard to this endeavour is to provide a compile-time, arbitrary-precision integer that looks like this: typedef big_integer<positive_sign,2,9,9,7,9,2,4,5,8> metric_speed_of_light; typedef big_integer<negative_sign,9,0,0,0,0,0,0,0,0,0> coulomb_constant; Once I get it to work, I should be able to roll out big_fraction and big_rational the same way I did fraction_c and rational_c, after which I'll consider building fixed_c on top of big_rational. The challenge I face now, of course, is getting big_integer to work. Right now I could wrap the digits in a vector_c, with the unspecified digits set to -1, then perhaps use iter_fold_backward to convert it into a little-endian digit sequence so I could process the value more easily. I believe the issue there is that the user has to #define BOOST_MPL_LIMITS_VECTOR_SIZE before #including big_integer in order to increase the precision beyond 10 digits, right? What should I do in this case? BTW, how and where should I provide MPL lambda support, if at all? Admittedly I have no idea how that works. Cromwell Enage __________________________________ Do you Yahoo!? Friends. Fun. Try the all-new Yahoo! Messenger. http://messenger.yahoo.com/

Cromwell Enage writes:
I now have a working implementation of fraction_c and rational_c, as well as arithmetic metafunctions that operate on them, at <http://groups.yahoo.com/group/boost/files/mpl_math.zip> (tested on MinGW 3.2 and MSVC 7.1).
Cromwell, This is very exciting! Unfortunately, I'm currently swamped and can't comment in detail. I try to get back to you on this within a week or so. -- Aleksey Gurtovoy MetaCommunications Engineering

An update of my work is available in the <http://groups.yahoo.com/group/boost/files/> directory, filename mpl_math.zip (MSVC 6 SP 5 burped an error when compiling mpl_fraction.cpp; I didn't bother trying the other two examples on it.) --- Cromwell Enage <sponage@yahoo.com> wrote:
My current focus with regard to this endeavour is to provide a compile-time, arbitrary-precision integer that looks like this:
typedef big_integer<positive_sign,2,9,9,7,9,2,4,5,8> metric_speed_of_light; typedef big_integer<negative_sign,9,0,0,0,0,0,0,0,0,0> coulomb_constant;
Actually it's called "big_integral" now.
Once I get it to work, I should be able to roll out big_fraction and big_rational the same way I did fraction_c and rational_c, after which I'll consider building fixed_c on top of big_rational.
The negate, add, and multiply metafunctions are now operational for big_integral, but I still need to tackle divide, modulus, greatest_common_divisor, and least_common_multiple before I can build the other number meta-types.
Right now I could wrap the digits in a vector_c, with the unspecified digits set to -1, then perhaps use iter_fold_backward to convert it into a little-endian digit sequence so I could process the value more easily. I believe the issue there is that the user has to #define BOOST_MPL_LIMITS_VECTOR_SIZE before #including big_integer in order to increase the precision beyond 10 digits, right? What should I do in this case?
I haven't made the precision completely independent of BOOST_MPL_LIMITS_VECTOR_SIZE, but I've given the user the ability to increase the precision by setting the BOOST_MPL_LIMITS_BIG_INTEGRAL_DIGIT_COUNT_FACTOR macro to whatever value she deems fit. The default value is 4, which when multiplied with BOOST_MPL_LIMITS_VECTOR_SIZE amounts to a default precision of 40 digits. A minor issue with this setup is that when setting BOOST_MPL_LIMITS_BIG_INTEGRAL_DIGIT_COUNT_FACTOR the user should also set BOOST_MPL_BIG_INTEGRAL_DIGIT_TYPE to the appropriate integer type (unsigned int by default). Should I use MPL's sizeof construct to remove this potential dependency? BTW, I'm looking forward to the new MPL docs. Maybe then I'll learn to use MPL lambda + iterators properly instead of using Boost.PP as a crutch. Cromwell Enage __________________________________ Do you Yahoo!? Friends. Fun. Try the all-new Yahoo! Messenger. http://messenger.yahoo.com/

I'll be going on vacation for a couple of weeks. Before I do, here's my latest work on the subject: <http://groups.yahoo.com/group/boost/files/mpl_math.zip> I've provided the remaining big_integral operations (divide, modulus, gcd, and lcm) as well as an absolute-value operation. I've had to split the example program up, however, because the compilers I'm using choke if I don't. In fact, only MSVC 7.1, which is optimized for Windows, succeeds with the single example lcm operation. AFAICT the only way I can improve the performance of the operations is by using more efficient algorithms than the schoolbook ones I'm currently using. --- Aleksey Gurtovoy <agurtovoy@meta-comm.com> wrote:
Within the wrapper struct, in form of a conversion operator, so we can write something like
typedef float_<2,718281828> e; std::cout << e;
I've gotten the syntax down to: typedef rational_c<int,positive_sign,4,5,8>::type shoe_size; std::cout << shoe_size(); This is also true for fraction_c and big_integral. --- I wrote:
BTW, how and where should I provide MPL lambda support, if at all? Admittedly I have no idea how that works.
Okay, I now remember what my Lisp teacher told me about lambda expressions, and from delving deeper into the source I have a better idea how MPL lambda works. I should be back from vacation right after the new version of MPL becomes available. Cromwell Enage __________________________________ Do you Yahoo!? Yahoo! Mail - 50x more storage than other providers! http://promotions.yahoo.com/new_mail
participants (3)
-
Aleksey Gurtovoy
-
Cromwell Enage
-
David Abrahams