Сумма десятичного числа в Java - PullRequest
4 голосов
/ 24 марта 2011

У меня проблема с управлением десятичным числом в java (JDK 1.4).

У меня есть два двойных числа первого и второго (как вывод отформатированного String ). Я делаю сумму между кулаком и секундой и получаю число с большим количеством десятичных цифр!

   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.5d ), потому что я не уверен в аналогичной ситуации (когда я начал кодировать, спецификации говорили о 0.1d для сравнения значение). Если это единственное решение, значение 0,9d является самым безопасным значением для этой проблемы?

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

Какая-то идея (если есть возможность проверить строку кода;))?

Ответы [ 5 ]

3 голосов
/ 24 марта 2011

Эта ошибка будет немного зависеть от вашей версии Java (и я вижу, что вы используете немного старую версию). Однако, независимо от версии Java, для достижения наилучших результатов, когда вас особенно беспокоит десятичная точность в java, вы должны использовать класс BigDecimal для своих значений.

Это то, что финансовые приложения используют для обработки валюты, а также то, что многие промышленные Java-приложения используют, когда необходима точность.

РЕДАКТИРОВАТЬ: я вижу много правильных комментариев, что это решение с небольшим падением производительности (также зависит от количества операций, которые вы делаете в первую очередь). И действительно, если это единственное единственное место, где вы сталкиваетесь с проблемой и вам не нужна точность после нее, тогда вы идете к обходному пути. Но если это происходит часто и в более чем одном месте, или если вы думаете, что вы можете расширить свое приложение в будущем, я бы использовал более безопасный BigDecimal.

3 голосов
/ 24 марта 2011

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

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. Это часто предпочтительное решение для многих разработчиков, но последний вариант ИМХО. ;)

0 голосов
/ 24 марта 2011

Я согласен с Питером, я тоже не вижу, чтобы это произошло.Однако, если это продолжает происходить с вами, и если число десятичных дробей известно и зафиксировано в вашем сценарии использования в любом случае, использование «int» может быть решением, это даже быстрее, чем операции с плавающей точкой.Конечно, для представлений вам придется преобразовать их в плавающие точки.

0 голосов
/ 24 марта 2011

Я тестировал, и на моей машине все правильно (проверено в Java 1.6).На вашем месте я бы протестировал модификатор strictfp для метода, который выполняет указанные выше операции:

public static strictfp void main(String[] args)

Проблема может быть связана с версией Java, ОС, процессором, который вы используете

0 голосов
/ 24 марта 2011

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

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

Для двойных и плавающих чисел вы должны использовать <компаратор, чтобы проверить, достаточно ли близки 2 числа, чтобы считать их равными </p>

См. Это для более подробной информации -> Какой самый эффективный способ сравнения с плавающей запятой и двойного сравнения?

...