2010-10-24 31 views
9

Estoy escribiendo un programa que consiste en un ciclo while que lee dos dobles y los imprime. El programa también imprime lo que es el número más grande y cuál es el número más pequeño.Comparando dobles

este es el código que tengo hasta ahora.

int main() 
{ 

            // VARIABLE DECLARATIONS 

    double a; 
    double b; 

    while (a,b != '|')    //WHILE A & B DO NOT EQUAL '|' 
    { 
     cin >>a >>b; 
     cout << a << b << "\n" ; 


     if (a<b)     //IF A<B: SMALLER VALUE IS A 
     cout << "The smaller value is:" << a << endl 
      << "The larger value is:" << b << endl ; 

     else if (b<a)    //ELSE IF B<A 
      cout << "The smaller value is:" << b << endl 
       << "The larger value is:" << a << endl ; 
     else if (b==a) 
      cout << "The two numbers you entered are equal." << "\n" ; 

    } 
} 

El siguiente paso es tener el programa escribir "los números son casi iguales" si los dos números difieren en menos de 1,0/10000000. ¿Cómo haría esto?

+5

Huele la tarea, así que por favor agregue la etiqueta * tarea * si ese es el caso. –

+2

Es curioso lo lejos que puede viajar ese olor ... –

+8

@ user484955: ¿Realmente sabes qué significa 'while (a, b! = '|')'? –

Respuesta

11

std::abs(a - b) < 0.000001

Por supuesto, cambia la constante con lo que se tiene en cuenta "casi".

+0

@Peter: Solucionado. Gracias. –

+2

Además, tenga en cuenta que el "error" se ampliará con el tamaño de ay b. – Fredrik

+0

@Fredrik: Es cierto, pero el OP especificó "dos números difieren en menos de SOMECONSTANT". Por supuesto, podría improvisar algo que compruebe si los exponentes en los valores eran los mismos, y luego usar una constante similar solo en las mantisas. –

4

prueba sólo si difieren en menos de esa cantidad :)

if (std::abs(a - b) < 1.0/10000000) 
    cout << "The numbers are almost equal.\n"; 
+1

+1 porque me olvidé de la parte de abs . –

+0

Intenté esto pero aparece un error que dice: "Llamada de sobrecargada '(doble)' es ambigua" – Capkutay

+1

Capkutay: ¿Incluyó # '? Eso es necesario para que 'std :: abs' funcione. También es posible que tenga un 'using namespace std;' en alguna parte, lo cual es una mala práctica, y podría causar un conflicto entre 'std :: abs' y' abs' en el espacio de nombres global. ('abs' está permitido, pero no es obligatorio, para colocarse en el espacio de nombres global, porque se hereda de C) Corríjalo eliminando' using namespace std; 'y calificando sus llamadas a la biblioteca estándar, según corresponda. –

2
if (a * 1.0000001 > b && a < b*1.0000001) 

Se puede añadir un valor de error (el 1,0/10000000.0), pero por lo general es mejor utilizar un multiplicador, por lo que la la comparación es con el mismo nivel de precisión.

+1

Las otras respuestas van por el valor de error absoluto.Esto podría ser incluso más rápido, pero solo es exacto cuando conoce el orden de magnitud de los dos valores. – winwaed

0
abs(a - b) < 1.0/10000000 
21

Aquí es cómo iba a comprobar la igualdad, sin un "fudge factor":

if (
    // Test 1: Very cheap, but can result in false negatives 
    a==b || 
    // Test 2: More expensive, but comprehensive 
    std::abs(a-b)<std::abs(std::min(a,b))*std::numeric_limits<double>::epsilon()) 
    std::cout << "The numbers are equal\n"; 

Explicación

La primera prueba es una simple comparación. Por supuesto, todos sabemos que comparar los valores de precisión doble puede hacer que se los considere desiguales, incluso cuando sean lógicamente equivalentes.

Un valor de punto flotante de doble precisión puede contener los quince dígitos más significativos de un número (en realidad ≈15.955 dígitos). Por lo tanto, queremos llamar dos valores iguales si (aproximadamente) coinciden sus primeros quince dígitos. Para decirlo de otra manera, queremos llamarlos iguales si están dentro de un épsilon escalado entre sí. Esto es exactamente lo que calcula la segunda prueba.

Puede elegir agregar más margen de acción que un épsilon de escala simple, debido a que los errores de coma flotante más importantes se arrastran como resultado del cálculo iterativo. Para ello, agregue un factor de error en el lado derecho de la comparación de la segunda prueba:

double error_factor=2.0; 

if (a==b ||   
    std::abs(a-b)<std::abs(std::min(a,b))*std::numeric_limits<double>::epsilon()* 
        error_factor) 
    std::cout << "The numbers are equal\n"; 

no le puede dar un valor fijo para el error_factor, ya que dependerá de la cantidad de error que se arrastra en su cómputos. Sin embargo, con algunas pruebas, debe poder encontrar un valor razonable que se adapte a su aplicación. Tenga en cuenta que agregar un factor de error (arbitrario) basado únicamente en la especulación lo pondrá de nuevo en el territorio del factor de dulce de azúcar.

Resumen

Usted puede envolver la siguiente prueba en un (n inline) Función:

inline bool logically_equal(double a, double b, double error_factor=1.0) 
{ 
    return a==b || 
    std::abs(a-b)<std::abs(std::min(a,b))*std::numeric_limits<double>::epsilon()* 
        error_factor; 
} 
+3

+1 para el uso de 'std :: numeric_limits' –

+0

Creo que debe reemplazar el" /1.e-15 "a" /1.e15 ", de lo contrario, el RHS de la comparación es enorme. – regomodo

+1

@regomodo, buena captura! –

1

Si desea que la prueba a escala con a y b, podría intentar probar abs (a/b-1) < e, donde e es su número pequeño favorito, como 0.001. Pero esta condición es realmente asimétrica en ayb, por lo que puede funcionar para decir que a está cerca de b, pero b no está cerca de a. Eso sería malo.Es mejor hacer abs (log (a/b)) < e, donde e, de nuevo, es tu pequeño número favorito. Pero los logaritmos presentan cómputo adicional, por no mencionar terroríficos estudiantes universitarios en todas partes.

+0

1. "número pequeño favorito" probablemente debería ser 'std :: numeric_limits :: epsilon()', 2. Log no es probablemente la mejor manera de manejar esto de todos modos. Si desea una comparación similar a esta, debería verificar el signo y el exponente para ver si son iguales y luego comparar los significados que permiten algún valor de error. (Use 'frexp' para hacer eso; debería funcionar mucho mejor que' log' o 'log10 ') –

+0

Una de las mejores respuestas es que se compara con a y b, sin embargo, es mejor multiplicar que dividir. Para un b podría ser cero. abs (ab) CashCow

0

También estoy leyendo el libro, ya que no tuvimos la oportunidad de std :: abs, hice algo por el estilo:

int main() 
{ 
double i1,i2; 
while(cin>> i1 >> i2){ 

if (i1<i2) { 
     if ((i2-i1)<=0.0000001) cout << "Almost equal!"<<endl; 
     else cout << "the smaller value is: "<< i1 << " the larger value is: " << i2 <<endl; 
} 
if (i1>i2) { 
     if ((i1-i2)<=0.0000001) cout << "Almost equal!"<<endl; 
     else cout << "the smaller value is: "<< i2 << " the larger value is: " << i1 <<endl; 
} 


else if (i1==i2) cout << "the value : "<< i1 << " And the value : " << i2 << " are equal!"<<endl; 

} 
}