Сравнение двойного с нулем - PullRequest
18 голосов
/ 19 ноября 2011

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

Если вы запустите его и введете цифры "1 -5 8 -4", вы получите следующий вывод:

1 -5 8 -4
p=-0.333333, q=0.074074
disc1=0.001372, disc2=-0.001372
discriminant=0.00000000000000001236
Discriminant is greater than zero.

Я знаю, что проблема возникает из-за того, что вычисления с двойными числами не точны. Обычно дискриминант должен быть равен 0, но в конечном итоге он будет выглядеть примерно как 0,00000000000000001236.

Мой вопрос: каков наилучший способ избежать этого? Должен ли я проверить, находится ли число между эпсилонами окрестности нуля? Или есть лучший и более точный способ?

Заранее благодарю за ответы.

import java.util.Scanner;

class Cubical {
    public static void main(String[] args) {
        // Declare the variables.
        double a, b, c, d, p, q, gamma, discriminant;

        Scanner userInput = new Scanner(System.in);
        a = userInput.nextDouble();
        b = userInput.nextDouble();
        c = userInput.nextDouble();     
        d = userInput.nextDouble();

        // Calculate p and q.
        p = (3*a*c - b*b) / (3*a*a);
        q = (2*b*b*b) / (27*a*a*a) - (b*c) / (3*a*a) + d/a;

        // Calculate the discriminant.
        discriminant = (q/2)*(q/2) + (p/3)*(p/3)*(p/3);

        // Just to see the values.
        System.out.printf("p=%f, q=%f\ndisc1=%f, disc2=%f\ndiscriminant=%.20f\n", p, q, (q/2)*(q/2), (p/3)*(p/3)*(p/3), (q/2)*(q/2) + (p/3)*(p/3)*(p/3));

        if (discriminant > 0) {
            System.out.println("Discriminant is greater than zero.");
        }
        if (discriminant == 0) {
            System.out.println("Discriminant is equal to zero.");
        }
        if (discriminant < 0) {
            System.out.println("Discriminant is less than zero.");
        }
    }
}

Ответы [ 4 ]

17 голосов
/ 19 ноября 2011

Самая простая проверка эпсилон

if(Math.abs(value) < ERROR)

более сложный пропорционален значению

if(Math.abs(value) < ERROR_FACTOR * Math.max(Math.abs(a), Math.abs(b)))

В вашем конкретном случае вы можете:

if (discriminant > ERROR) {
    System.out.println("Discriminant is greater than zero.");
} else if (discriminant < -ERROR) {
    System.out.println("Discriminant is less than zero.");
} else {
    System.out.println("Discriminant is equal to zero.");
}
14 голосов
/ 19 ноября 2011

Должен ли я проверить, находится ли число между эпсилон-окрестностями нуля?

Точно

4 голосов
/ 19 ноября 2011

Вот решение, которое является точным, когда входные значения являются целыми числами, хотя, вероятно, оно не самое практичное.

Вероятно, оно также будет хорошо работать на входных значениях, которые имеют конечное двоичное представление (например, 0,125 делает, но 0,1 нет).

Хитрость: уберите все деления из промежуточных результатов и делите только один раз в конце.Это делается путем отслеживания всех (частичных) числителей и знаменателей.Если дискриминант должен быть 0, тогда его числитель будет равен 0. Здесь нет ошибки округления, если значения при промежуточных сложениях находятся в пределах ~ 2 ^ 45 друг от друга (что обычно имеет место).

// Calculate p and q.
double pn = 3 * a * c - b * b;
double pd = 3 * a * a;

double qn1 = 2 * b * b * b;
double qd1 = 27 * a * a * a;
double qn2 = b * c;
double qn3 = qn1 * pd - qn2 * qd1;
double qd3 = qd1 * pd;
double qn = qn3 * a + d * qd3;
double qd = qd3 * a;

// Calculate the discriminant.
double dn1 = qn * qn;
double dd1 = 4 * qd * qd;
double dn2 = pn * pn * pn;
double dd2 = 27 * pd * pd * pd;

double dn = dn1 * dd2 + dn2 * dd1;
double dd = dd1 * dd2;
discriminant = dn / dd;

(проверяется только на предоставленных входных значениях, поэтому скажите, если что-то не так)

3 голосов
/ 19 ноября 2011

возможно BigDecimal стоит взглянуть на ...

http://download.oracle.com/javase/1.4.2/docs/api/java/math/BigDecimal.html

Вы можете включить режим округления в операции деления

...