Округление произвольной длины с плавающей запятой до n цифр

#c #rounding #arbitrary-precision

#c #округление #произвольная точность

Вопрос:

Я уже прочитал большинство вопросов SO, связанных с «округлением до n цифр». Я считаю, что этот вопрос является отдельным, потому что он больше связан с округлением чисел с плавающей запятой произвольной длины.

 0.56714329040978387299996866221035554975381578718651250813513107922304579308668456669321944696175229455773495728285284421635951688
  

Приведенное выше число является примером типов чисел, с которыми я работаю.

Я понимаю, как округлить число в C , это довольно просто. Однако я не понимаю, как я могу округлить произвольно длинные числа до n цифр.

Например, у меня может быть число длиной 192 цифры, которое мне нужно округлить до 84 цифр, или число, состоящее из 1831 цифры, которое мне нужно округлить до 293 цифр. Как это можно сделать с помощью одной функции (или аналогичной)?

Псевдокод для ясности, но на самом деле я использую многопоточный cpp_dec_float для чисел с плавающей запятой произвольной точности вместо стандартных чисел с плавающей запятой:

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

Другая проблема, с которой я столкнулся, заключается в попытке округлить очень длинные числа с плавающей запятой до небольшого количества цифр.

Например, если у меня есть число длиной 1000 цифр, и я хочу округлить его до n цифр, я должен сделать что-то вроде floor(num * 10^1000)/10^1000) . Это не работает, потому что 10 ^ 1000 очень велико. Решением этой проблемы было бы многократное умножение и деление с меньшими показателями.

Комментарии:

1. Просто хочу убедиться, что я правильно понял вопрос. Какой из двух: округление до количества цифр (относительная точность) или «округление до единицы» (абсолютная точность). Например. round(0.253e 5,2digits)==0.25e 5 (относительный) vs round(0.253e 5, 100units)==0.253e 5) ?

2. @AdrianColomitchi Я больше говорю 0.0236 о возвратах с округлением до 3 цифр 0.024 , если это то, о чем вы говорите?

3. «На самом деле я использую многопоточность Boost для чисел с плавающей запятой произвольной точности» — вам нужно быть более конкретным.

4. Вот оно: round(x, precision_unit)=trunc(x/precision_unit 0.5)*precision_unit; — код в моем ответе

5. У вас 0.5671432... нет произвольной длины с плавающей запятой. Это текстовое представление значения, и компилятор преобразует его в значение с плавающей запятой. Значения с плавающей запятой в каждой известной мне системе не поддерживают 192 цифры точности, а тем более 1000 (обычно double может содержать около 16 цифр). Как только вы превысите точность значения с плавающей запятой, любые отображаемые вами цифры по сути бессмысленны. Итак, что бы вы ни хотели сделать со всеми этими цифрами, вам придется делать с текстовым представлением, а не с типами с плавающей запятой.

Ответ №1:

[Отредактировано — лучшее решение]

Выполнение округления может основываться на:

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

Что-то вроде:

   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;
  

Результат:

 3.1415926535897932384626433833
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068
  

Комментарии:

1. Не используйте std::endl , если вам не нужны дополнительные вещи, которые он делает. 'n' завершает строку.