Java Представление Ошибка Представления Double - PullRequest
0 голосов
/ 03 июня 2018

Время от времени я вижу некоторые ошибки округления, которые вызваны тем, что некоторые значения приведены ниже, как показано в двух примерах ниже.

// floor(number, precision)

double balance = floor(0.7/0.1, 3) // = 6.999 
double balance = floor(0.7*0.1, 3) // = 0.069

Проблема, конечно, в 0.7/0.1 и 0.7*0.1это не совсем то число, которое должно быть из-за ошибок представления [см. примечание ниже].

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

double balance = floor(0.7/0.1 + 1e-10, 3) // = 7.0
double balance = floor(0.7*0.1 + 1e-10, 3) // = 0.07

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

Например, если был способ получить оценкуошибка (как в representation - number) или, по крайней мере, ее признак (representation > number или нет), что было бы полезно определить, в каком направлении я должен исправить результат, прежде чем применять floor.

Любой другой обходной путь, который вы можете придумать, очень приветствуется.

ПРИМЕЧАНИЕ: Я знаю, что настоящая проблема в том, что я использую double, и у него есть ошибки представления.Пожалуйста, воздержитесь от того, чтобы говорить что-либо вроде того, что я должен хранить баланс долго ((long) Math.floor(3931809L/0.080241D) одинаково ошибочно).Я также пытался использовать BigDecimal, но производительность сильно снизилась (это приложение реального времени).Кроме того, обратите внимание, что я не очень обеспокоен распространением небольших ошибок во времени, я делаю много вычислений, как те, что приведены выше, но я начинаю с нового номера баланса каждый раз (я делаю, возможно, 3 из этих операций, прежде чем вернуться и начать заново).

РЕДАКТИРОВАТЬ: Чтобы сделать это ясно, это единственная операция, которую я делаю, и я повторяю ее 3 раза на том же балансе.Например, я беру баланс в долларах США и конвертирую его в рубли, затем в иены, затем в евро, и я возвращаю баланс и начинаю сначала (с новым номером баланса, т.е. ошибка округления не передается, кроме как наэти 3 операции).Значения не ограничены, за исключением того, что они являются положительными числами (т. Е. В диапазоне [0, + inf)), а точность всегда ниже 8 (8 десятичных цифр, т. Е. 0,00000001 - наименьший баланс, с которым мне когда-либо приходилось иметь дело).

Ответы [ 2 ]

0 голосов
/ 03 июня 2018

Двоичное число с плавающей запятой (двойная точность) имеет точность 53 бита или приблизительно 15,95 десятичных цифр.

Пусть r - Реальное, а d - double, ближайший к r

epsilon (r) = | r - d |находится в диапазоне от 0 до r * 2 floor (log2 (r)) -53

и, если вам приходится иметь дело с числами в диапазоне от 0 до N, томаксимальное значение эпсилона во всем диапазоне будет примерно равно:

N * 2 этаж (log2 (N)) - 53

Когда вы выполнитевычисление, вам нужно оценить совокупную ошибку для всех этапов вычисления.Сложение, умножение и деление относительно «безопасно».Например, с умножением:

Пусть r 1 = d 1 + e 1 и r 2 = d 2 + e 2

r 1 * r 2 = (d 1 + e 1 ) * (d 2 + e 2 ) = d 1 * d 2 2 * е 1 + д 1 * е 2 + е 1 * е 2

Если значения эпсилона уже велики, термин e 1 * e 2 исчезает относительно других, и эпсилон идетбыть ≤ 2 * max (| d 1 |, | d 2 |) * max (| e 1 |, | e 2 |).

(я думаю. Я очень долго не делал этого в гневе.)

Однако вычитание имеет неприятные свойства;см. теорему 9 статьи Гольдберга!

Используемая вами функция floor также немного сложна.

Пусть r = d + e

epsilon (floor (r)) = | floor (r, 3) - floor (d, 3) |

, что составляет ≤ max (| потолок (e, 3) |, 10 -3 )

0 голосов
/ 03 июня 2018

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

Нет эпсилона, который гарантированно работал бы во всех случаях 1 .Период.

Если вы проанализируете (математически 2 ) вычисления, которые выполняет ваше приложение, то может определить значение эпсилона, которое будет работатьдля тебя.

Но учтите, что существует опасность в многократном «округлении» ошибок при многоэтапном вычислении.В итоге вы можете получить неправильный ответ.Вот что говорит математика.

Наконец, спросите себя: если законно / безопасно просто вносить корректировки на основе эпсилона, почему (после 50 с лишним лет) типичные ручные калькуляторы все еще настаивают, что 1.0 / 3 * 3 равно 0.9999999999....


1 - Позволяет быть ясным.Вы не пытались указать, каковы ваши «дела».Поэтому я предполагаю, что вы имеете в виду все возможные вычисления.

2 - Анализ осложняется тем, что эпсилон между числом Real и соответствующим плавающим двоичным числомпредставление (например, «двойное») зависит от двоичной величины числа.

...