Это забавная проблема.
Идея ответа Timons заключается в том, что вы указываете epsilon, который представляет собой наименьшую точность, которая может быть двойной. Если вы знаете в своем приложении, что вам никогда не понадобится точность ниже 0.00000001, то то, что он предлагает, достаточно, чтобы получить более точный результат, очень близкий к истине. Полезно в приложениях, где они знают свою максимальную точность (например, для финансирования валютных преференций и т. Д.)
Однако основная проблема с попыткой ее округления состоит в том, что когда вы делите на коэффициент, чтобы перемасштабировать его, вы фактически вводите еще одна возможность для точных проблем. Любая манипуляция двойниками может вводить проблемы неточности с разной частотой. Особенно, если вы пытаетесь раунда в очень значащей цифры (так что ваши операнды < 0), например, если вы запустите следующее с Timons кодом:
System.out.println(round((1515476.0) * 0.00001)/0.00001);
будет приводить к 1499999.9999999998
, где цель здесь заключается в округлить на единицах 500000 (т. е. мы хотим 1500000)
Фактически единственный способ быть уверенным, что вы устранили неточность, - это пройти через BigDecimal для масштабирования. например
System.out.println(BigDecimal.valueOf(1515476.0).setScale(-5, RoundingMode.HALF_UP).doubleValue());
Используя сочетание стратегии эпсилон и стратегии BigDecimal даст вам точный контроль над вашей точностью. Идея, являющаяся epsilon, очень близка, и BigDecimal устранит любую неточность, вызванную изменением масштаба. Хотя использование BigDecimal снижает ожидаемую производительность вашего приложения.
Было указано, что последний шаг использования BigDecimal для его масштабирования не всегда необходим для некоторых случаев использования, когда вы можете определить, что нет входного значения, которое окончательное подразделение может повторно ввести ошибку. В настоящее время я не знаю, как правильно определить это, поэтому, если кто-нибудь знает, как тогда я был бы рад услышать об этом.
Невозможно вообще «избежать» арифметических ошибок с плавающей запятой. Количество бит, используемых при представлении числа, всегда будет конечным. Все, что вы можете сделать, это использовать типы данных с более высокой точностью (бит). – 2008-10-07 17:17:49
Это правда. Я отредактирую свой ответ, чтобы более точно отразить использование BigDecimal. – 2008-10-07 17:29:32
Я добавлю, что BigDecimal_division_ нужно обрабатывать немного иначе, чем +, -, *, поскольку по умолчанию он генерирует исключение, если он не может вернуть точное значение (1/3, например). В подобной ситуации я использовал: BigDecimal.valueOf (a) .divide (BigDecimal.valueOf (b), 25, RoundingMode.HALF_UP) .doubleValue(), где 25, если максимальные цифры точности (более чем необходимы двойному результату). – 2011-07-12 16:05:12