2010-10-24 20 views
9

Sto scrivendo un programma che consiste in un ciclo while che legge due doppi e li stampa. Il programma stampa anche quale sia il numero più grande e quale sia il numero più piccolo.Confronto di doppi

questo è il codice che ho finora.

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

    } 
} 

Il passo successivo è avere il programma scrivere out "i numeri sono quasi uguali" se i due numeri differiscono di meno di 1,0/10000000. Come lo farei?

+5

Gli odori fanno i compiti, quindi per favore aggiungi il tag * compiti a casa * se è il caso. –

+2

Divertente quanto lontano può viaggiare quell'odore ... –

+8

@ user484955: Sai veramente cosa significa "while (a, b! = '|')"? –

risposta

11

std::abs(a - b) < 0.000001

Naturalmente, sostituire la costante con qualunque cosa si considera "quasi".

+0

@Peter: corretto. Grazie. –

+2

Inoltre, si noti che l '"errore" si scalerà con le dimensioni di a e b. – Fredrik

+0

@Fredrik: vero, ma l'OP ha specificato "due numeri differiscono di meno di SOMECONSTANT". Certo, si potrebbe mettere insieme qualcosa che controlla se gli esponenti sui valori erano gli stessi, e quindi usando una costante simile solo sulla mantide. –

4

prova Proprio se differiscono da inferiore a tale importo :)

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

+1 perché ho dimenticato la parte abs . –

+0

Ho provato questo, ma sto ricevendo un errore che dice "Call of overloaded abs (double)" è ambiguo " – Capkutay

+1

Capkutay: hai incluso # '? È necessario che 'std :: abs' funzioni. È anche possibile che tu abbia un 'using namespace std;' da qualche parte, che è una cattiva pratica, e potrebbe causare un conflitto tra 'std :: abs' e' abs' nello spazio dei nomi globale. ('abs' è permesso, ma non obbligatorio, per essere inserito nello spazio dei nomi globale, perché è ereditato da C) Risolto rimuovendo' using namespace std; 'e qualificando le chiamate alla libreria standard come appropriato. –

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

È possibile aggiungere un valore di errore (il vostro 1.0/10.000.000,0), ma in generale è meglio usare un moltiplicatore, in modo che il il confronto è allo stesso livello di accuratezza.

+1

Le altre risposte stanno andando per il valore di errore assoluto.Questo potrebbe anche essere più veloce, ma è accurato solo quando si conosce l'ordine di grandezza dei due valori. – winwaed

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

Ecco come ho iniziato ad esaminare per l'uguaglianza, senza un "fattore di correzione":

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

Spiegazione

Il primo test è un semplice confronto. Naturalmente sappiamo tutti che confrontare i valori a doppia precisione può far sì che vengano considerati ineguali, anche quando sono logicamente equivalenti.

Un valore in virgola mobile a doppia precisione può contenere le quindici cifre più significative di un numero (in realtà ≈15.955 cifre). Pertanto, vogliamo chiamare due valori uguali se (approssimativamente) corrispondono alle loro prime quindici cifre. Per dirla in un altro modo, vogliamo chiamarli uguali se si trovano all'interno di un epsilon in scala l'uno dell'altro. Questo è esattamente ciò che calcola il secondo test.

È possibile scegliere di aggiungere più margine di manovra rispetto a un singolo epsilon ridimensionato, a causa di errori di virgola mobile più significativi che si insinuano come risultato del calcolo iterativo. Per fare questo, aggiungere un fattore di errore sul lato destro del confronto del secondo prova:

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

non posso dare un valore fisso per la error_factor, poiché dipenderà dalla quantità di errore che si insinua nella vostra calcoli. Tuttavia, con alcuni test dovresti essere in grado di trovare un valore ragionevole adatto alla tua applicazione. Tieni presente che l'aggiunta di un fattore di errore (arbitrario) basato sulla sola speculazione ti riporterà nel territorio del fattore fudge.

Sommario

Puoi avvolgere il seguente test in un (n inline) Funzione:

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 per l'uso di 'std :: numeric_limits' –

+0

penso che è necessario sostituire il "/1.e-15" a "/1.e15" altrimenti la RHS del confronto è enorme. – regomodo

+1

@regomodo, buona cattura! –

1

Se si desidera che il test in scala con A e B, si potrebbe provare il test abs (a/b-1) < e, dove e è il tuo numero minuscolo preferito, come 0.001. Ma questa condizione è in realtà asimmetrica in aeb, quindi può funzionare per dire che è vicino a b, ma b non è vicino a a. Sarebbe male.È meglio fare abs (log (a/b)) < e, dove e, di nuovo, è il tuo numero minuscolo preferito. Ma i logaritmi presentano calcoli extra, per non parlare degli studenti universitari terrificanti ovunque.

+0

1. "preferito piccolo number' dovrebbe probabilmente essere' std :: :: numeric_limits epsilon() ', 2. Registro non è probabilmente il modo migliore per gestire questa situazione in ogni caso. Se vuoi un confronto simile a questo, dovresti controllare il segno e l'esponente per l'uguaglianza e quindi confrontare i significati tenendo conto di qualche valore di errore (usa 'frexp' per farlo - dovrebbe funzionare molto meglio di' log' o 'log10 ') –

+0

Una delle risposte migliori in quanto si confronta con a e b, tuttavia è meglio moltiplicare che dividere. Per uno b potrebbe essere zero. abs (ab) CashCow

0

Sto anche leggendo il libro, dal momento che non siamo riusciti a std :: abs, ho fatto qualcosa di simile:

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; 

} 
}