2009-10-05 14 views
31
non floating

Quello che voglio dire è la seguente:doppio controllo variabile se contiene un numero intero, e il punto

double d1 =555; 
    double d2=55.343 

voglio essere in grado di dire che D1 è un numero intero, mentre non è d2. C'è un modo semplice per farlo in c/C++?

+0

Ci sono hack per farlo funzionare, ma il modo in cui un doppio rappresenta il suo valore significa che esso è molto probabilmente non immagazzinando un intero come pensi che lo farebbe. Può memorizzare 555 come 554.99999998 o qualcosa di divertente come quello. Non c'è modo di dirlo davvero, a meno che tu non abbia i valori di origine originali e non lo capisca da lì. float e double sono di precisione limitata. –

+5

@ Ape-inago: In realtà, un doppio che è * assegnato * un valore intero sarà effettivamente uguale a quel valore. E l'aggiunta, la sottrazione e la moltiplicazione per i doppi a valore intrinseco producono anche un doppio con valore integrale. Tuttavia, una volta moltiplicato per un doppio arbitrario o dividendolo per un doppio, tutte le scommesse sono disattivate. – rlbond

+3

rlbond, assumendo che il numero intero in questione sia sufficientemente piccolo. – avakar

risposta

56

Uso std::modf:

double intpart; 
modf(value, &intpart) == 0.0 

non si convertono a int! Il numero 1.0e+300 è anche un numero intero.

Modifica: come sottolinea Pete Kirkham, passare 0 come secondo argomento non è garantito dallo standard per funzionare, richiedendo l'uso di una variabile dummy e, sfortunatamente, rendendo il codice molto meno elegante.

+14

Non avevo idea che 'modf' esistesse ora. – GManNickG

+2

concordato. +1 per i nugget nella libreria standard C. Anche se userei 'NULL' invece di' 0', ma capisco che questo è un problema piuttosto controversa in C++ per nessuna buona ragione. –

+1

dovrebbe leggere 'modf (value, 0) == 0' come' modf() 'restituisce la parte frazionaria! – Christoph

-1

Un codice di esempio snipped che lo fa:

if ( ABS(((int) d1) - (d1)))< 0.000000001) 

cout <<"Integer" << endl; 

else 

cout <<"Flaot" << endl; 

EDIT: cambiato per riflettere codice corretto.

+1

Che dire di 100.01? – Calyth

+0

se il numero # è 1.0000000001 (come può essere restituito, ad esempio il backend DB), non funzionerà. – DVK

+2

Questo è ** sbagliato ** poiché non è possibile utilizzare% sul doppio d1 – Jacob

3

Come su

if (abs(d1 - (round(d1))) < 0.000000001) { 
    printf "Integer\n"; /* Can not use "==" since we are concerned about precision */ 
} 

fisso fino a lavorare con arrotondamento per riflettere bug Anna trovato

soluzioni alternative:

if ((d1 - floor(d1) < 0.000000001) || (d1 - floor(d1) > 0.9999999999)) { 
    /* Better store floor value in a temp variable to speed up */ 
    printf "Integer\n"; /* Can not use "==" since we are concerned about precision */ 
} 

C'è anche un altro con l'assunzione di piano, sottraendo 0,5 e prendendo abs() di quello e rispetto a 0.499999999 ma immagino che non sarà un miglioramento delle prestazioni importanti.

+0

Se d = 15.999.997, che cos'è il piano di ritorno (d)? Non penso che funzioni. –

+5

I numeri interi (almeno fino a una certa dimensione) sono rappresentati esattamente in qualsiasi implementazione reale di numeri in virgola mobile a cui riesco a pensare, quindi non penso che sia necessario preoccuparsi della precisione per questo particolare problema. –

+2

E il caso in cui d1 = 0.999999999999999? – Anna

3
int iHaveNoFraction(double d){ 
    return d == trunc(d); 
} 

Ora, non sarebbe C, se non avesse circa 40 anni di revisioni linguistiche ...

In C, == rendimenti int ma in C++ restituisce bool. Almeno sulla mia distro Linux (Ubuntu) devi dichiarare double trunc(double); o puoi compilare con -std=c99, o dichiarare la macro di livello, il tutto per ottenere <math.h> per dichiararlo.

+2

Forse il tipo restituito è bool, poiché restituisce un valore booleano? –

+0

trunc() è migliore di floor(). Funziona con numeri negativi. –

+0

Alcon, buon punto, potrebbe essere C++. Certamente, in C, * è * 'int'. Ho chiarito le cose ... – DigitalRoss

9

Supponendo di avere la libreria di cmath <math.h>, è possibile controllare il numero con il numero floor. Se il numero potrebbe essere negativo, assicurati di ottenere prima il absolute.

bool double_is_int(double trouble) { 
    double absolute = abs(trouble); 
    return absolute == floor(absolute); 
} 
+7

Per motivi di sanità mentale, consiglio di riconsiderare la scelta del nome del parametro. – Alan

+0

@Alan, di bell'aspetto. Lo ha cambiato – tj111

+1

Penso che non sia necessario prima passare al valore assoluto. – updogliu

0

prova:

bool isInteger(double d, double delta) 
{ 
    double absd = abs(d); 

    if(absd - floor(absd) > 0.5) 
     return (ceil(absd) - absd) < delta; 

    return (d - floor(absd)) < delta; 
} 
1
#include <math.h> 
#include <limits> 

int main() 
{ 
    double x, y, n; 
    x = SOME_VAL; 
    y = modf(x, &n); // splits a floating-point value into fractional and integer parts 
    if (abs(y) < std::numeric_limits<double>::epsilon()) 
    { 
    // no floating part 
    } 
} 
1

ne dici di questo?

if ((d1 - (int)d1) == 0) 
    // integer 
3

avakar aveva quasi ragione - utilizzare modf, ma il dettaglio era spento.

modf restituisce la parte frazionaria, quindi il test dovrebbe essere che il risultato di modf è 0.0.

modf accetta due argomenti, il secondo dei quali dovrebbe essere un puntatore dello stesso tipo del primo argomento. Passare NULL o 0 causa un errore di segmentazione nel runtime g ++. Lo standard non specifica che passare 0 sia sicuro; potrebbe essere che capiti di lavorare sulla macchina di Avakar ma non farlo.

È inoltre possibile utilizzare fmod(a,b) che calcola il modulo ab passando a 1.0. Anche questo dovrebbe dare la parte frazionaria.

#include<cmath> 
#include<iostream> 

int main() 
{ 
    double d1 = 555; 
    double d2 = 55.343; 

    double int_part1; 
    double int_part2; 

    using namespace std; 

    cout << boolalpha; 
    cout << d1 << " " << modf (d1, &int_part1) << endl; 
    cout << d1 << " " << (modf (d1, &int_part1) == 0.0) << endl; 
    cout << d2 << " " << modf (d2, &int_part2) << endl; 
    cout << d1 << " " << (modf (d2, &int_part2) == 0.0) << endl; 
    cout << d2 << " " << modf (d2, &int_part2) << endl; 
    cout << d1 << " " << (modf (d2, &int_part2) == 0.0) << endl; 

    cout << d1 << " " << fmod (d1, 1.0) << endl; 
    cout << d1 << " " << (fmod (d1, 1.0) == 0) << endl; 
    cout << d2 << " " << fmod (d2, 1.0) << endl; 
    cout << d2 << " " << (fmod (d2, 1.0) == 0) << endl; 


    cout.flush(); 

    modf (d1, 0); // segfault 

} 
+0

+1, ho controllato lo standard e in effetti richiede un puntatore a un oggetto come secondo argomento di 'modf'. Grazie. – avakar

+0

Oh, e non devi "flush", passare "endl" lo fa per te. :-) – avakar

+0

+1 causa un errore di bus su GCC anche su OS X. Se solo gli standard fossero più chiari! –

0

Qui di seguito hanno il codice per il test D1 e D2 mantenerlo molto semplice. L'unica cosa da verificare è se il valore della variabile è uguale allo stesso valore convertito in un tipo int. Se questo non è il caso, allora non è un numero intero.

#include<iostream> 
using namespace std; 

int main() 
{ 
    void checkType(double x); 
    double d1 = 555; 
    double d2 = 55.343;   
    checkType(d1); 
    checkType(d2); 
    system("Pause"); 
    return 0; 
} 
void checkType(double x) 
{ 
    if(x != (int)x) 
    { 
      cout<< x << " is not an integer "<< endl; 
    } 
    else 
    { 
     cout << x << " is an integer " << endl; 
    } 
}; 
+1

Si prega di utilizzare la formattazione del codice SO. Se una linea inizia con quattro spazi, SO la formatterà come codice. –

5

Ipotizzando una C99 e IEEE-754 ambiente compatibile,

(trunc(x) == x) 

è un'altra soluzione, e sarà (sulla maggior parte delle piattaforme) sono leggermente migliori prestazioni rispetto modf perché ha bisogno solo per produrre la parte intera . Entrambi sono completamente accettabili.

Si noti che trunc produce un risultato a precisione doppia, quindi non è necessario preoccuparsi di conversioni di tipo non compreso nell'intervallo come si farebbe con (int)x.


Edit: come @pavon punti fuori in un commento, potrebbe essere necessario aggiungere un altro controllo, a seconda se o non vi preoccupate per l'infinito, e che cosa risultato che si desidera ottenere se x è infinito.

+0

Si noti che questo test considererà +/- infinito come numeri interi. – pavon

+0

@pavon: È * quasi * ragionevole dire che l'infinito è un numero intero pari in IEEE 754 (nel senso che tutti i numeri in virgola mobile sufficientemente grandi sono anche interi, quindi anche l'infinito "dovrebbe essere", questo è un non senso, ma accattivanti assurdità). –

+0

Bene, tutti i test in questa pagina considerano un ± ∞ un numero intero. –

0

In molti calcoli si sa che i risultati in virgola mobile avranno un piccolo errore numerico che può risultare da un numero di moltiplicazioni.

Quindi quello che si può veramente desiderare di trovare è la domanda è questo numero all'interno di dire 1e-5 di un valore intero. In tal caso penso che funzioni meglio:

bool isInteger(double value) 
{ 
    double flr = floor(value + 1e-5); 
    double diff = value - flr; 
    return diff < 1e-5; 
} 
0

Ho affrontato una domanda simile. come ho bisogno di arrotondare il doppio in ogni caso, che è quello che ho trovato a lavorare:

double d = 2.000000001; 
int i = std::round(d); 
std::fabs(d-i) < 10 * std::numeric_limits<double>::epsilon() 
Problemi correlati