Как избежать ошибок округления с двойными числами? - PullRequest
0 голосов
/ 04 мая 2019

Я использую Math.sin для вычисления тригонометрии в Java с точностью до 3 десятичных знаков. Однако, когда я вычисляю значения, которые должны привести к Integer, я получаю 1.0000000002 вместо 1.

Я пытался использовать

 System.out.printf(Locale.ROOT, "%.3f ", v);

, которая решает проблему превращения 1.000000002 в 1.000.

Однако, когда я вычисляю числа, которые должны привести к 0 , вместо этого получаю -1.8369701987210297E-16 и использую

  System.out.printf(Locale.ROOT, "%.3f ", v);

печатает -0,000, когда мне нужно, чтобы оно было 0,000.

Есть идеи, как избавиться от этого отрицательного знака?

Ответы [ 2 ]

4 голосов
/ 04 мая 2019

Давайте начнем с этого:

Как избежать ошибок округления с двойными числами?

По сути, вы не можете. Они присущи численным расчетам с использованием типов с плавающей запятой. Поверьте мне ... или найдите время, чтобы прочитать эту статью:

В этом случае другая вещь, которая вступает в игру, состоит в том, что тригонометрические функции реализуются путем вычисления конечного числа шагов бесконечного ряда с конечной точностью (то есть арифметикой с плавающей запятой). Javadoc для класса Math оставляет некоторую «комнату для маневра» в отношении точности математических функций. Стоит прочитать javadocs, чтобы понять ожидаемые границы ошибки.

Наконец, если вы вычисляете (например) sin π / 2 , вам нужно подумать, насколько точным является ваше представление π / 2 .


Так что вы действительно должны спросить, как справиться с ошибкой округления, которая неизбежно случается.

В этом случае вы спрашиваете, как сделать так, чтобы пользователь вашей программы выглядел так, как если бы не было ошибки округления. Есть два подхода к этому:

  • Оставь это в покое! Ошибки округления происходят, поэтому мы не должны лгать пользователям об этом. Лучше их воспитывать. (Честно говоря, это математика средней школы , и даже "остроконечный босс" должен понимать, что арифметика неточна.)

    Подпрограммы типа printf делают довольно хорошую работу. И -0,000, отображаемое в этом случае, на самом деле является правдивым ответом. Это означает, что вычисленный ответ округляется до нуля до 3 десятичных знаков, но на самом деле является отрицательным. Это на самом деле не трудно для кого-то с математикой средней школы, чтобы понять. Если вы это объясните.

  • Ли. Притворяться. Добавьте какой-то особый код для явного преобразования чисел от -0,0005 до нуля. Код, предложенный в комментарии

      System.out.printf(Locale.ROOT, "%.3f ", Math.round(v * 1000d) / 1000d);
    

    это еще один способ сделать работу. Но риск этого заключается в том, что ложь может быть опасной в некоторых обстоятельствах. С другой стороны, вы можете сказать, что настоящей проблемой ошибки является отображение чисел с точностью до 3 десятичных знаков.

0 голосов
/ 04 мая 2019

В зависимости от необходимой точности, вы можете умножить на X и разделить на X, где X равно X=10^y, а y требуется точность плавающего позиционирования.

...