Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > разница между 2-мя double


Автор: Pawl 12.11.2013, 08:55
Отнимаю от одного double value другое. Затем определяю, результат получился большим, меньшим или равным 0. Проблема в том, что при вычитании одинаковых double-чисел получается не всегда 0, а +0.0000... или -0.0000.... Пока выкрутился так, что сравниваю результат вычитания не с 0, а с некой погрешностью:
Код

if (result >= 0.000001) {
    ...
} else if (result <= -0.000001) {
    ...
} else {
    ....
}

Но сдается мне, это костыль. Может, кто поделится лучшим способом?
Спасибо!

Автор: danilych 12.11.2013, 08:59
тип double не стоит использовать для точных вычислений - используйте BigDecimal

Автор: baldina 12.11.2013, 09:48
гм. а чем плох double?

Добавлено через 7 минут и 29 секунд
уточню: что вы понимаете под точными вычислениями? и нужно ли ТС именно это, учитывая оверхед BigDecimal...

Автор: Pawl 12.11.2013, 10:08
Цитата(danilych @  12.11.2013,  08:59 Найти цитируемый пост)
тип double не стоит использовать для точных вычислений - используйте BigDecimal

К сожалению, код не мой. Надо пользоваться тем, что есть.
Цитата(baldina @  12.11.2013,  09:48 Найти цитируемый пост)
 что вы понимаете под точными вычислениями? 

Поясню. Вот, я задал погрешность 0.000001, а разность получилась, к примеру, 0.0000001, т. е. не 0, а мой код это примет за 0. Конкретно для моего ТЗ Точные вычисления не суть важны, моего костыля хватит. Мне интересно, решаема ли данная проблема с double в принципе, или в подобных случаях корректно использовать только BigDecimal?

Автор: baldina 12.11.2013, 10:38
Цитата(Pawl @  12.11.2013,  10:08 Найти цитируемый пост)
решаема ли данная проблема с double в принципе

в принципе все примерно так, как вы и делаете. т.к. double по определению не является точным значением, все сравнения нужно производить с учетом погрешности.
обычно это делают в виде
Код

double epsilon = 0.000001;
if (Math.abs (result) < epsilon) {
 // result == 0
}
else {
 // result != 0
}
...
if (Math.abs (result0-result1) < epsilon) {
  // result0 == result1
} else if (result0 > result1) {
} else {
 // result1 > result0
}

можно написать функции сравнения. это не костыль, это следствие представления http://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%B0_%D1%81_%D0%BF%D0%BB%D0%B0%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%B9_%D0%B7%D0%B0%D0%BF%D1%8F%D1%82%D0%BE%D0%B9 в компьютере

Добавлено через 8 минут и 12 секунд
Pawl, если расчеты финансовые, то BigDecimal удобнее, т.к. автоматически производит округление по одному из правил (в т.ч. банковское). А производительность вычислений в бухгалтерских расчетах обычно не является критичным параметром.

Автор: Pawl 12.11.2013, 12:50
Цитата(baldina @  12.11.2013,  10:38 Найти цитируемый пост)
в принципе все примерно так, как вы и делаете

ОК, учту на будущее. Всем спасибо.

Автор: Mirkes 12.11.2013, 22:19
Вообще говоря поймать +-0 в double можно, нашел этот прием а коде Arrays.
Код

        final long NEG_ZERO_BITS = Double.doubleToLongBits(-0.0d);
        /*
             * The sort is done in three phases to avoid the expense of using
             * NaN and -0.0 aware comparisons during the main sort.
             */

        /*
             * Preprocessing phase:  Move any NaN's to end of array, count the
             * number of -0.0's, and turn them into 0.0's.
             */
        int numNegZeros = 0;
        int i = 0, n = a.length;
        while (i < n) {
            if (a[i] != a[i]) {
                double swap = a[i];
                a[i] = a[--n];
                a[n] = swap;
                int sw = index[i];
                index[i] = index[n];
                index[n] = sw;
            } else {
                if (a[i] == 0 && Double.doubleToLongBits(a[i]) == NEG_ZERO_BITS) {
                    a[i] = 0.0d;
                    numNegZeros++;
                }
                i++;
            }



Правда судя по тексту и +0 и -0 таки равны просто 0. Нужно будет проверить.

Автор: Pawl 12.11.2013, 22:51
Цитата(Mirkes @  12.11.2013,  22:19 Найти цитируемый пост)
Вообще говоря поймать +-0 в double можно

проблема в том, что иногда double - такой же double дает не +-0, а, к примеру, -5.551115123125783E-17. В таком случае ваш пример не поможет...
При выводе на экран, от "страшных" чисел и ненужных минусов я нашел способ избавиться при помощи методов класса java.text.NumberFormat.

Автор: baldina 13.11.2013, 10:54
Цитата(Mirkes @  12.11.2013,  22:19 Найти цитируемый пост)
судя по тексту и +0 и -0 таки равны просто 0

просто 0 это +0
между собой они равны (в смысле результат сравнения true), хотя двоичное представление разное

Автор: Mirkes 13.11.2013, 12:10
Цитата(Pawl @  12.11.2013,  22:51 Найти цитируемый пост)
проблема в том, что иногда double - такой же double дает не +-0, а, к примеру, -5.551115123125783E-17. В таком случае ваш пример не поможет...
При выводе на экран, от "страшных" чисел и ненужных минусов я нашел способ избавиться при помощи методов класса java.text.NumberFormat.

А вот это уже говорит о том, что это не совсем одинаковые double  smile 
Если Вас интересуют double равные с заданной точностью, то Ваше решение с введением некоего эпсилон единственно возможное. Хотя, пожалуй еще возможно сравнение по относительной величине:
Код

    result = d1-d2;
    avg = (d1+d2)/2;
    epsilon = 0.000000001;
    if (result<-epsilon*avg) {
       //меньше
    }else if (resultЮepsilon*avg) {
       //больше
    } else {
       //равны
    }

Такой относительный подход предпочтительнее если Вы заранее не знаете порядка величин, поскольку если у Вас два числа порядка единицы дают разницу порядка 10^-15, то если Вы умножите оба своих числа на 10^20, то разность будет порядка 10^5.

Автор: Pawl 13.11.2013, 12:39
Цитата(Mirkes @  13.11.2013,  12:10 Найти цитируемый пост)
Хотя, пожалуй еще возможно сравнение по относительной величине

Да, хорошая мысль!

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)