Re: [Boost-bugs] [Boost C++ Libraries] #12527: cpp_bin_float: Anal fixation. Part 3. Double rounding when result of convert_to<double>() is a subnormal

Subject: Re: [Boost-bugs] [Boost C++ Libraries] #12527: cpp_bin_float: Anal fixation. Part 3. Double rounding when result of convert_to<double>() is a subnormal
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2016-11-14 01:07:19


#12527: cpp_bin_float: Anal fixation. Part 3. Double rounding when result of
convert_to<double>() is a subnormal
-------------------------------+----------------------------
  Reporter: Michael Shatz | Owner: johnmaddock
      Type: Bugs | Status: reopened
 Milestone: To Be Determined | Component: multiprecision
   Version: Boost 1.62.0 | Severity: Problem
Resolution: | Keywords:
-------------------------------+----------------------------

Comment (by Michael Shatz):

 I did a little more research and now I know why your new version is slow.
 That's because on both of platforms that I did the tests (x64/gcc 5.3.0
 on MINGW and x64/msvc 19.00.24210) library function std::ldexp() is much
 slower than can be reasonably expected.
 So, for not to wide cpp_bin_float types, execution time of convert_to<>()
 is almost directly proportional to number of ldexp() calls.

 There are circumstances when ldexp() is inlined by compiler and then it
 runs several times faster, but so far I was unable to figure out what they
 are.

 Below is a variant of eval_convert_to() that is ideologically similar to
 your code - all rounding is done with integer arithmetic, but unlike you
 code it calls ldexp() exactly once. On my test platforms it is ~4 times
 faster than yours.
 Unfortunately, it is not complete: it only handles cases in which
 std::numeric_limits<Float>::digits <= 62. Thus, works very well for IEEE
 double and float, but even x87 'long double' is too much, as well as
 anything wider than that.

 Nevertheless, look at it. Even if you can't extend it to wider types,
 IMHO, IEEE double and float are sufficiently important to be special-
 cased.


 {{{
 template <class Float, unsigned Digits, digit_base_type DigitBase, class
 Allocator, class Exponent, Exponent MinE, Exponent MaxE>
 inline typename boost::enable_if_c<boost::is_float<Float>::value>::type
 eval_convert_to(Float *res, const cpp_bin_float<Digits, DigitBase,
 Allocator, Exponent, MinE, MaxE> &original_arg)
 {
   Float ret = 0;
   switch (eval_fpclassify(original_arg)) {
     case FP_INFINITE:
       ret = std::numeric_limits<Float>::infinity();
       break;

     case FP_NAN:
       *res = std::numeric_limits<Float>::quiet_NaN();
       return;

     case FP_ZERO:
       break;

     default:
     { // == normal, because cpp_bin_float does not support subnormal
       if (original_arg.exponent() >=
 std::numeric_limits<Float>::min_exponent -
 std::numeric_limits<Float>::digits - 1) {
         if (original_arg.exponent() <=
 std::numeric_limits<Float>::max_exponent-1) {
           int e = original_arg.exponent();
           cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE,
 MaxE> y = original_arg;
           y.exponent() = 63;
           y.sign() = 0;

           uint64_t bits;
           eval_convert_to(&bits, y);

           int nhbits = std::numeric_limits<Float>::digits;
           if (e < std::numeric_limits<Float>::min_exponent-1)
             nhbits = e - std::numeric_limits<Float>::min_exponent + 1 +
 std::numeric_limits<Float>::digits;
           // round
           uint64_t lbits = bits << nhbits;
           uint64_t hbits = (bits >> 1) >> (63-nhbits); // shift by
 (64-nhbits) that works for nhbits in range [0..63]
           lbits |= (hbits & 1); // assure that tie
 rounded to even
           const uint64_t BIT63 = uint64_t(1) << 63;
           if (lbits == BIT63) {
             cpp_bin_float<Digits, DigitBase, Allocator, Exponent, MinE,
 MaxE> truncY;
             truncY = bits;
             if (!eval_eq(truncY, y))
               lbits |= 1;
           }
           hbits += (lbits > BIT63);
           ret =
 std::ldexp(static_cast<Float>(static_cast<int64_t>(hbits)), e + 1 -
 nhbits);
         } else {
           // overflow
           ret = std::numeric_limits<Float>::infinity();
         }
       }
     }
       break;
   }
   *res = original_arg.sign() ? -ret : ret;
 }

 }}}

-- 
Ticket URL: <https://svn.boost.org/trac/boost/ticket/12527#comment:15>
Boost C++ Libraries <http://www.boost.org/>
Boost provides free peer-reviewed portable C++ source libraries.

This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:20 UTC