Подтвердить что ты не робот

Сравнение двойного с нулем

Я новичок в 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.");
        }
    }
}
4b9b3361

Ответ 1

Простейшая проверка эпсилона

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.");
}

Ответ 2

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

Точно

Ответ 3

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

Он, вероятно, также отлично работает на входных значениях, которые имеют конечное двоичное представление (например, 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;

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