Сумма десятичных чисел в java
У меня есть проблема для управления десятичным числом в java (JDK 1.4).
У меня есть два двойных числа первое и второе (как результат форматированной строки). Я делаю сумму между кулаком и секундой, и я получаю число с большим количеством десятичных цифр!
final double first=198.4;//value extract by unmodifiable format method
final double second=44701.2;//value extract by unmodifiable format method
final double firstDifference= first+second; //I receive 44899.598 instead of 44899.6
final double calculatedDifference=44900.1; // comparison value for the flow
final double error=firstDifference-calculatedDifference;// I receive -0.50390605 instead 0.5
if(Math.abs(error)<=0.5d)){
//I must enter in this branch but the error not allows the correct flow!!!
}
/***
* the flow of program is uncorrect and there's a very visible bug in business view
*/
Я предпочитаю не увеличивать пороговое значение (0.5 d), потому что я не уверен в подобной ситуации (когда я начал кодировать, спецификации говорили о 0.1 d в качестве сравнения значение). Если это единственное решение, то значение 0.9 d является самым безопасным значением для этой задачи?
Как я могу разрешить эту ситуацию? Я думал, что эта проблема выводится с помощью двойных переменных, но с плавающей точкой у меня та же проблема.
Какая-то идея (имея некоторую проверенную строку кода, если это возможно ;))?
5 ответов:
Вы можете получить ошибку округления, но я не вижу ее здесь.
final double first=198.4;//value extract by unmodifiable format method final double second=44701.2;//value extract by unmodifiable format method final double firstDifference= first+second; //I receive 44899.6 final double calculatedDifference=44900.1; // comparison value for the flow final double error=firstDifference-calculatedDifference;// I receive -0.5 if(Math.abs(error)<=0.5d){ // this branch is entered. System.out.println(error); }
Отпечатки
Есть два способа справиться с этим в более общем плане. Можно определить ошибку округления, например-0.5
private static final double ERROR = 1e-9; if(Math.abs(error)<=0.5d + ERROR){
Или использовать округление
final double firstDifference= round(first+second, 1); // call a function to round to one decimal place.
Или использовать целые числа с фиксированной точностью
final int first=1984;// 198.4 * 10 final int second=447012; // 44701.2 * 10 final int firstDifference= first+second; //I receive 448996 final int calculatedDifference=449001; // comparison value for the flow final int error=firstDifference-calculatedDifference;// I receive -5 if(Math.abs(error)<=5){ // this branch is entered. System.out.println(error); }
Или вы можете использовать BigDecimal. Это часто предпочтительное решение для многих разработчиков, но последний вариант ИМХО. ;)
Эта ошибка будет немного зависеть от вашей версии Java (и я вижу, что вы используете немного старую версию). Однако, независимо от версии Java, для получения наилучших результатов, когда вы особенно беспокоитесь о десятичной точности в java, вы должны использовать класс
Это то, что финансовые приложения используют для обработки валюты, а также то, что многие промышленные приложения Java используют, когда точность необходима.BigDecimal
для ваших значений.EDIT: я вижу много правильных комментариев, что это решение поставляется с небольшим снижением производительности (также зависит от количества операций, которые вы делаете в первую очередь). И действительно, если это единственное место, где вы сталкиваетесь с проблемой, и вы не заботитесь о точности после нее, то ya, пойти на обходной путь. Но если это происходит часто и в нескольких местах, или если вы думаете, что можете расширить свое приложение в будущем, я бы использовал более безопасный
BigDecimal
.
Двойной номер магазина предметов в степенях 2. Невозможно точно представить множество чисел с точки зрения степеней 2, и поэтому вы получаете проблемы округления. Та же проблема и при использовании поплавков.
Если вы хотите уменьшить их, используйте десятичный тип для чисел, это должно оказаться более точным.
Для парных и плавающих чисел необходимо использовать компаратор
Смотрите это для более подробной информации -> что является ли это наиболее эффективным способом для плавающего и двойного сравнения?
Я протестировал, и на моей машине все правильно (протестировано в java 1.6). На вашем месте я бы проверил модификатор strictfp на метод, который имеет вышеуказанные операции:
public static strictfp void main(String[] args)
Проблема может быть связана с версией java, ОС, процессором, который вы используете
Я согласен с Питером,но и этого не вижу. Однако, если это продолжает происходить с вами, и если число десятичных знаков известно и зафиксировано в вашем usecase в любом случае, использование " int " может быть решением, это даже быстрее, чем операции с плавающей запятой. Для представлений, которые вы затем должны были бы преобразовать в плавающие точки, конечно.