Сумма десятичных чисел в 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 4

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, вы должны использовать класс BigDecimal для ваших значений.

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

EDIT: я вижу много правильных комментариев, что это решение поставляется с небольшим снижением производительности (также зависит от количества операций, которые вы делаете в первую очередь). И действительно, если это единственное место, где вы сталкиваетесь с проблемой, и вы не заботитесь о точности после нее, то ya, пойти на обходной путь. Но если это происходит часто и в нескольких местах, или если вы думаете, что можете расширить свое приложение в будущем, я бы использовал более безопасный BigDecimal.

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

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

Для парных и плавающих чисел необходимо использовать компаратор

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

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

public static strictfp void main(String[] args)

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

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