2011-11-19 10 views
18

Soy nuevo en Java y he estado tratando de implementar un algoritmo para encontrar las raíces de una ecuación cúbica. El problema surge cuando calculo el discriminante e intento verificar dónde cae en relación con cero.Comparando un doble contra cero

Si lo ejecuta e introducir los números "1 8 -5 -4", la salida es el siguiente:

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

Sé que el problema surge debido a los cálculos con los dobles no son precisos. Normalmente el discriminante debe ser 0, pero termina siendo algo así como 0.000000000000000.

Mi pregunta es, ¿cuál es la mejor manera de evitar esto? ¿Debo verificar si el número cae entre un barrio épsilon de cero? ¿O hay una forma mejor y más precisa?

Gracias de antemano por sus respuestas.

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

Respuesta

16

El cheque épsilon más simple es

if(Math.abs(value) < ERROR) 

una más compleja es proporcional al valor

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

En su caso específico se puede:

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

@hattenn: Para el OP, la segunda sugerencia de Peter es el camino a seguir. –

+0

¿Qué valor es mejor para ERROR? –

+0

@LunaKong el error depende del contexto. Podría ser 1e-12 hasta 1e-3 o podría necesitar ser otra cosa. –

14

debo comprobar si el número se sitúa entre un barrio épsilon de cero?

Exactamente

+3

1 que respetar la palabra, no cualificados (no hay 'peros') respuesta a la pregunta directa. –

4

Aquí hay solución que sea preciso en los valores de entrada son enteros , aunque probablemente no sea el más práctico.

Probablemente también funcionará bien en los valores de entrada que tienen una representación binaria finita (por ejemplo, 0.125 hace, pero 0.1 no).

El truco: elimine todas las divisiones de los resultados intermedios y solo divida una vez al final. Esto se hace mediante el seguimiento de todos los numeradores y denominadores (parciales). Si el discriminante debe ser 0, entonces el numerador será ser 0. Aquí no hay error de redondeo, siempre y cuando los valores en las adiciones intermedias estén dentro de una magnitud de ~ 2^45 entre sí (que es generalmente el caso).

// 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; 

(sólo se comprueba en los valores de entrada proporcionados, así que dime si algo está mal)

+0

He eliminado algunas de las divisiones de los resultados intermedios y ahora funciona mucho mejor. Gracias por el aviso. – hattenn