Время от времени я вижу некоторые ошибки округления, которые вызваны тем, что некоторые значения приведены ниже, как показано в двух примерах ниже.
// 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 - наименьший баланс, с которым мне когда-либо приходилось иметь дело).