Введение
Нет достаточной конечной точности.
Задача, поставленная в вопросе, эквивалентна:
- Какая точность p гарантируетчто преобразование любого рационального числа x в p десятичных цифр и затем в число с плавающей точкой дает число с плавающей точкой, ближайшее x (или, в случае связилюбой из двух ближайших x )?
Чтобы увидеть, что это эквивалентно, обратите внимание, что деление BigDecimal
, показанное в вопросе, возвращает num
/ div
ввыбранное количество знаков после запятой. Затем возникает вопрос, может ли увеличение этого количества десятичных разрядов повысить точность результата. Ясно, что если число с плавающей запятой ближе x , чем результат, точность может быть улучшена. Таким образом, мы спрашиваем, сколько десятичных знаков необходимо, чтобы гарантировать получение ближайшего числа с плавающей запятой (или одного из двух связанных).
Так как BigDecimal
предлагает выбор методов округления, я рассмотрюдостаточно ли одного из них. Я предполагаю, что для преобразования в число с плавающей запятой используется округление до ближайшего числа связей (даже при использовании BigDecimal
при преобразовании в Double
или Float
). Я даю доказательство, используя формат бинарного кода IEEE-754, который Java использует для Double
, но это доказательство применимо к любому двоичному формату с плавающей запятой путем изменения значения 2 52 , используемого ниже, на 2 w -1 , где w - количество бит в значении.
Доказательство
Один из параметров для BigDecimal
деление - метод округления. Java BigDecimal
имеет несколько методов округления . Нам нужно только рассмотреть три, ROUND_UP, ROUND_HALF_UP и ROUND_HALF_EVEN. Аргументы для остальных аналогичны приведенным ниже с использованием различных симметрий.
В дальнейшем предположим, что мы преобразуем в десятичную форму, используя любую большую точность p . То есть p - это число десятичных цифр в результате преобразования.
Пусть m - рациональное число 2 52 +1 + ½-10 р . Два соседних двоичных числа 64 m равны 2 52 + 1 и 2 52 + 2. m ближе к первому, так что это результат, который нам требуется от преобразования m сначала в десятичную, а затем в плавающую точку.
В десятичной, m - это 4503599627370497.4999…, где есть p −1 с последующими 9 с. При округлении до p значащих цифр с ROUND_UP, ROUND_HALF_UP или ROUND_HALF_EVEN, результат будет 4503599627370497.5 = 2 52 + 1 + ½. (Признайте, что в позиции, где происходит округление, отбрасывается 16 конечных 9, что фактически составляет долю .9999999999999999 относительно позиции округления. В ROUND_UP любая ненулевая отклоненная сумма вызывает округление. В ROUND_HALF_UP и ROUND_HALF_EVEN, aвыброшенная сумма больше ½ в этой позиции вызывает округление в большую сторону.)
2 52 + 1 + ½ одинаково близко к соседним двоичным числам64 2 52 + 1 и2 52 + 2, поэтому метод округления до ближайших связей к четным дает 2 52 + 2.
Таким образом, результат равен 2 52 + 2, что не является двоичным значением64, ближайшим к m .
Следовательно, конечной точности p не достаточно для правильного округления всех рациональных чисел.