Boost logo

Boost :

Subject: Re: [boost] [review] Multiprecision review (June 8th - 17th, 2012)
From: Steven Watanabe (watanabesj_at_[hidden])
Date: 2012-06-25 00:33:08


AMDG

I really haven't had time to go through this
library as thoroughly as I would have liked,
but, since it's the last day of the review:
*I vote to accept Multiprecision into Boost*

Here are my comments on the code so far.
It's not very much, since I only got
through a few files. I'll try to expand
on this as I have time, but no promises.

Revision: sandbox/big_number_at_79066

concepts/mp_number_architypes.hpp:

* The file name is misspelled. It should
  be "archetypes."

* The #includes aren't quite right. The
  file does not use trunc, and does use
  fpclassify.

* line 28: mpl::list requires #include <boost/mpl/list.hpp>
* line 79: stringstream requires #include <sstream>

depricated:

* Should be deprecated.

detail/functions/constants.hpp:

* line 12: typedef typename has_enough_bits<...> pred_type;
  If you're going to go through all this trouble to make
  sure that you have a type with enough bits, you shouldn't
  assume that unsigned has enough. IIRC, the standard
  only guarantees 16 bits for unsigned.

* line 38: if(digits < 3640)
  Ugh. I'm assuming that digits means binary digits
  and 3640 = floor(log_2 10^1100)

* line 51: I would appreciate a brief description
  of the formula. This code isn't very readable.

* line 105: It looks like this is using the formula
  e \approx \sum_0^n{\frac{1}{i!}} = (\sum_0^n\frac{n!}{i!})/n!
  and induction using the rule:
  \sum_0^n\frac{n!}{i!} =
    (\sum_0^(n-1)\frac{(n-1)!}{i!}) * n + 1

  Please explain the math or point to somewhere
  that explains it.

* line 147: ditto for pi

* line 251: calc_pi(result, ...digits2<...>::value + 10);
  Any particular reason for +10? I'd prefer
  calc_pi to do whatever adjustments are
  needed to get the right number of digits.

detail/functions/pow.hpp:

* line 18 ff: pow_imp
  I don't like the fact that this implementation
  creates log2(p) temporaries on the
  stack and multiplies them all together
  on the way out of the recursion. I'd
  prefer to only use a constant number
  of temporaries.

* line 31:
  I don't see how the cases > 1 buy you anything.
  They don't change the number of calls to eval_multiply.
  (At least, it wouldn't if the algorithm were
  implemented correctly.)

* line 47:
  ???: The work of this loop is repeated at every
  level of recursion. Please fix this function.
  It's a well-known algorithm. It shouldn't be
  hard to get it right.

* line 76: pow_imp(denom, t, -p, mpl::false_());
  This is broken for p == INT_MIN with twos-complement
  arithmetic.

* line 185: "The ldexp function..."
  I think you mean the "exp" function.

* line 273: if(x.compare(ll) == 0)
  This is only legal if long long is in T::int_types.
  Better to use intmax_t which is guaranteed to be in
  T::int_types.

* line 287: const bool b_scale = (xx.compare(float_type(1e-4)) > 0);
  Unless I'm missing something, this will always be
  true because xx > 1 > 1e-4 as a result of the
  test on line 240.

* line 332: if(&arg == &result)
  This is totally unnecessary, since you already copy
  arg into a temporary using frexp on line 348.

* line 351: if(t.compare(fp_type(2) / fp_type(3)) <= 0)
  Is the rounding error evaluating 2/3 important?
  I've convinced myself that it works, but it
  took a little thought. Maybe add a comment to
  the effect that the exact value of the boundary
  doesn't matter as long as its about 2/3?

* line 413: "The fabs function "
  s/fabs/log10/.

* line 464: eval_convert_to(&an, a);
  This makes the assumption that conversion
  to long long is always okay even if the
  value is out of the range of long long. This
  assumption makes me nervous.

* line 465: a.compare(an)
  compare(long long)

* line 474:
  eval_subtract(T, long long)
  Also, this value of da is never used. It
  gets overwritten on line 480 or 485.

* line 493: (eval_get_sign(x) > 0)
  This expression should always be true at this point.

* line 614: *p_cosh = x;
  cosh is an even function, so this is wrong when
  x = -\inf

* line 625: T e_px, e_mx;
  These are only used in the generic implementation.
  It's better to restrict the scope of variables
  to the region that they're actually used in.

* line 643: eval_divide(*p_sinh, ui_type(2));
  I notice that in other places you use ldexp for
  division by powers of 2. Which is better?

detail/mp_number_base.hpp:

* line 423: (std::numeric_limits<T>::digits + 1) * 1000L
  This limits the number of bits to LONG_MAX/1000.
  I'd like there to be some way to know that as
  long as I use less than k bits, the library
  won't have any integer overflow problems. k
  can depend on INT_MAX, LONG_MAX, etc, but the
  implementation limits need to be clearly documented
  somewhere. I really hate the normal ad hoc
  situation where you just ignore the problem and
  hope that all the values are small enough to avoid
  overflow.

Other notes:

Backend Requirements:

* Do we always require that all number types
  be assignable from intmax_t, uintmax_t,
  and long double? Why must an integer
  be assignable from long double?

* The code seems to assume that it's okay to
  pass int as the exp parameter of ldexp.
  These requirements don't mandate that this
  will work.

In Christ,
Steven Watanabe


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