Search code examples
c++roundingarbitrary-precision

Round arbitrary-length floats to n digits


I have already read most of the SO questions related to "rounding to n digits". I believe that this question is separate because it is more related to the rounding of arbitrary-length floats.

0.56714329040978387299996866221035554975381578718651250813513107922304579308668456669321944696175229455773495728285284421635951688

The number above is an example of the types of numbers I am working with.

I understand how to round a number in C++, it is fairly simple. However, I do not understand how I can round arbitrarily-long numbers to n digits.

For example, I may have a number that is 192 digits long that I need to round to 84 digits, or a number that is 1831 digits that I need to round to 293 digits. How can this be done using one function (or similar)?

Pseudocode for clarity, but I am actually using Boost multi-precision cpp_dec_float for arbitrary-precision floats instead of standard floats:

float round(float num, int digits){
    //returns num rounded to n digits
}

Another problem that I have come across is when trying to round very long floats to a small number of digits.

For example, if I have a number that is 1000 digits long, and I want to round it to n digits, I have to do something like floor(num * 10^1000)/10^1000). This doesn't work because 10^1000 is extremely large. A solution to this would be doing the multiplication and division multiple times with smaller exponents.


Solution

  • [Edited - better solution]

    Doing the rounding can go based on:

    round(x, precision_unit)=trunc(x/precision_unit+0.5)*precision_unit;
    

    Something like:

      using namespace boost::multiprecision;
    
      const uint lower_prec_digits=28;
    
      typedef number<cpp_dec_float<100>> higher_prec;
      typedef number<cpp_dec_float<lower_prec_digits>> lower_prec;
    
      const higher_prec eps_div=
          std::numeric_limits<
              number<cpp_dec_float<lower_prec_digits+1>>
          >::epsilon()
      ;
    
      higher_prec pi(
        "3.1415926535"
          "8979323846"
          "2643383279"
          "5028841971"
          "6939937510"
          "5820974944"
          "5923078164"
          "0628620899"
          "8628034825"
          "3421170679"
      );
    
      lower_prec round_pie=lower_prec(trunc(pi/eps_div+0.5)*eps_div);
    
      std::cout.precision(100);
      std::cout << round_pie << std::endl << pi << std::endl;
    

    Result:

    3.1415926535897932384626433833
    3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068