2009-10-05 8 views
31

Lo que quiero decir es lo siguiente:Comprobar variable doble si contiene un entero, y punto flotante no

double d1 =555; 
    double d2=55.343 

quiero ser capaz de decir que D1 es un número entero, mientras que D2 no lo es. ¿Hay una manera fácil de hacerlo en c/C++?

+0

Hay hacks para hacer que funcione, pero la forma en que un doble representa su valor significa que es muy posiblemente no se almacena un entero como crees que lo haría Puede almacenar 555 como 554.99999998 o algo divertido como ese. No hay forma de saberlo realmente, a menos que tengas los valores fuente originales y lo resuelvas a partir de ahí. float y double son de precisión limitada. –

+5

@ Ape-inago: en realidad, un doble al que * se le asigna * un valor entero en realidad equivaldrá a ese valor. Y sumar, restar y multiplicar por dobles de valor integral también produce un doble valor integral. Sin embargo, una vez que se multiplica por un doble arbitrario o se divide por un doble, todas las apuestas se desactivan. – rlbond

+3

rlbond, suponiendo que el entero en cuestión es suficientemente pequeño. – avakar

Respuesta

56

Uso std::modf:

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

no convierten a int! El número 1.0e+300 también es un número entero.

Editar: Como señala Pete Kirkham, pasa 0 porque el estándar no garantiza el segundo argumento, lo que requiere el uso de una variable ficticia y, por desgracia, hace que el código sea menos elegante.

+14

No tenía idea de que 'modf' existiera ahora. – GManNickG

+2

De acuerdo. +1 para nuggets en la biblioteca estándar de C. Aunque usaría 'NULL' en lugar de' 0' pero entiendo que es un problema bastante polémico en C++ sin una buena razón. –

+1

debería leer 'modf (value, 0) == 0' como' modf() '¡devuelve la parte fraccionaria! – Christoph

-1

Un código de ejemplo snipped que lo hace:

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

cout <<"Integer" << endl; 

else 

cout <<"Flaot" << endl; 

EDIT: lo cambió para reflejar código correcto.

+1

¿Qué hay de 100.01? – Calyth

+0

si el # es 1.0000000001 (como se puede devolver, por ejemplo, back-end de DB) no funcionará. – DVK

+2

Eso está ** mal ** ya que no puede usar% en el doble d1 – Jacob

3

¿Qué tal

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

fijo para trabajar utilizando redondeo para reflejar bug Anna encontró

soluciones alternativas:

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 */ 
} 

Hay también otra con la toma de suelo, restando 0,5 y tomando abs() de eso y en comparación con 0.499999999, pero creo que no será una gran mejora en el rendimiento.

+0

Si d = 15,999997, ¿qué devuelve el piso (d)? No creo que esto esté funcionando. –

+5

Los números enteros (al menos hasta cierto tamaño) están representados exactamente en cualquier implementación real de números en coma flotante que se me ocurra, así que no creo que haya necesidad de preocuparse por la precisión de este problema en particular. –

+2

¿Qué pasa con el caso donde d1 = 0.999999999999999? – Anna

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

Ahora, no sería C si no tenía unos 40 años de revisiones del lenguaje ...

En C, == vuelve int pero en C++ devuelve bool. Al menos en mi distribución de Linux (Ubuntu) necesita declarar double trunc(double); o puede compilar con -std=c99, o declarar el macro de nivel, todo con el fin de obtener <math.h> para declararlo.

+2

Tal vez haga que el tipo de retorno bool, ya que esto devuelve un booleano? –

+0

trunc() es mejor que floor(). Funciona para números negativos. –

+0

Alcon, buen punto, podría ser C++. Por supuesto, en C, it * is * 'int'. He aclarado cosas ... – DigitalRoss

9

Suponiendo que tiene la biblioteca cmath <math.h>, puede marcar el número en su floor. Si el número puede ser negativo, primero asegúrese de obtener el absolute.

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

Por motivos de cordura, recomiendo reconsiderar su elección del nombre del parámetro. – Alan

+0

@ Alan, bien cuidado. Cambiado. – tj111

+1

Creo que no hay necesidad de volver al valor absoluto primero. – updogliu

0

intento:

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

¿Qué tal esto?

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

avakar era casi correcto - use modf, pero el detalle estaba apagado.

modf devuelve la parte fraccionaria, por lo que la prueba debería ser que el resultado de modf es 0.0.

modf toma dos argumentos, el segundo de los cuales debe ser un puntero del mismo tipo que el primer argumento. Pasar NULL o 0 causa un error de segmentación en el tiempo de ejecución de g ++. La norma no especifica que pasar 0 es seguro; podría ser que funcione en la máquina de Avakar pero no lo haga.

También podría usar fmod(a,b) que calcula el a módulo b pasando 1.0. Esto también debería dar la parte fraccionaria.

#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, he comprobado el estándar y de hecho requiere un puntero a un objeto como segundo argumento para 'modf'. Gracias. – avakar

+0

Oh, y no tienes que 'flush', pasar' endl' hace eso por ti. :-) – avakar

+0

+1 causa un error de bus en GCC en OS X también. Si solo los estándares fueran más claros! –

0

A continuación tiene el código para probar d1 y d2, manteniéndolo muy simple. Lo único que tiene que probar es si el valor de la variable es igual al mismo valor convertido a un tipo int. Si este no es el caso, entonces no es un número entero.

#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

Utilice el formato de código SO. Si una línea comienza con cuatro espacios, SO lo formateará como código. –

5

Suponiendo un c99 y IEEE-754 entorno compatible,

(trunc(x) == x) 

es otra solución, y (en la mayoría de las plataformas) tienen un rendimiento ligeramente mejor que modf porque necesita sólo para producir la parte entera . Ambos son completamente aceptables.

Tenga en cuenta que trunc produce un resultado de doble precisión, por lo que no tiene que preocuparse por las conversiones fuera de rango como lo haría con (int)x.


Editar: como @pavon señala en un comentario, es posible que tenga que añadir otro cheque, dependiendo de si está o no se preocupan por el infinito, y qué resultado que desea obtener si x es infinito.

+0

Tenga en cuenta que esta prueba considerará +/- infinito como enteros. – pavon

+0

@pavon: Es * casi * razonable decir que el infinito es un entero par en IEEE 754 (en el sentido de que todos los números de coma flotante suficientemente grandes son incluso enteros, por lo que el infinito "debería ser" también; esto no tiene sentido, pero apelando tonterías). –

+0

Bueno, todas las pruebas en esta página considerarán que un ± ∞ es un número entero. –

0

En muchos cálculos, usted sabe que sus resultados de coma flotante tendrán un pequeño error numérico que puede resultar de varias multiplicaciones.

Entonces, lo que realmente querrá encontrar es que la pregunta es este número dentro, por ejemplo, 1e-5 de un valor entero. En ese caso, creo que esto funciona mejor:

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

Me enfrenté a preguntas similares. Como necesitaba para redondear el doble de todos modos, eso es lo que encuentro de trabajo:

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