¿Cómo determinar si un decimal / doble es un entero?

225

¿Cómo puedo saber si un valor decimal o doble es un entero?

Por ejemplo:

decimal d = 5.0; // Would be true
decimal f = 5.5; // Would be false

o

double d = 5.0; // Would be true
double f = 5.5; // Would be false

La razón por la que me gustaría saber esto es para poder determinar mediante programación si deseo generar el valor usando .ToString("N0")o .ToString("N2"). Si no hay un valor de punto decimal, entonces no quiero mostrar eso.

Jim Geurts
fuente

Respuestas:

410

Para los números de coma flotante, n % 1 == 0generalmente es la forma de verificar si hay algo más allá del punto decimal.

public static void Main (string[] args)
{
    decimal d = 3.1M;
    Console.WriteLine((d % 1) == 0);
    d = 3.0M;
    Console.WriteLine((d % 1) == 0);
}

Salida:

False
True

Actualización: como @Adrian López menciona a continuación, la comparación con un valor pequeñoepsilondescartará los cálculos erróneos de cálculo de punto flotante. Dado que la pregunta es sobredoublevalores, a continuación encontrará unarespuesta de prueba de cálculo de punto flotante más:

Math.Abs(d % 1) <= (Double.Epsilon * 100)
Mark Rushakoff
fuente
96
Eso funciona cuando el número comienza como un número entero, pero no necesariamente cuando el número es el resultado de un cálculo de punto flotante. ¿Qué tal algo como "(d% 1) <epsilon" donde la epsion tiene un valor pequeño?
Adrián López
99
Es una pena que la mejor respuesta en este hilo sea un comentario, en lugar de la respuesta aceptada. Buen Adrián.
starskythehutch
13
También creo que el comentario de Adrian anterior es la mejor respuesta. Para poner su consejo en el código formal de C #: if (Math.Abs ​​(n% 1) <Double.Epsilon) {// Haz algo si n es entero}.
Ruben Ramirez Padron
77
En mi humilde opinión, el operador de módulo y los números de coma flotante simplemente no se mezclan de ninguna manera útil. El código sugerido es increíblemente confuso dado el número de pulsaciones de teclas utilizadas y no funcionará en casi ningún lugar fuera de los lenguajes .NET. Apuesto a que también es mucho más lento que la forma correcta (que no usa ninguna división en absoluto). La forma correcta de hacer esto es usar Math.Abs(d-(int)d) < double.Epsilon. Como si todos hubiéramos aprendido en la primera semana de nuestra primera clase de programación en la universidad.
krowe2
99
En realidad, como se plantea la pregunta, esta respuesta es correcta y los comentarios son incorrectos. El OP no quiere saber si un doble es un número entero para fines matemáticos, sino cómo mostrarlo. Solo se deben mostrar los valores enteros exactos sin un punto decimal. Además, el comentario acerca de que el mod no es útil con punto flotante y no funciona fuera de .NET no está bien informado. Y (int)des un desastre que arrojará una excepción para la mayoría de los valores dobles.
Jim Balter
49

Hay muchas maneras de hacer esto. Por ejemplo:

double d = 5.0;
bool isInt = d == (int)d;

También puedes usar el módulo.

double d = 5.0;
bool isInt = d % 1 == 0;
Erik Funkenbusch
fuente
¿Sería uno de estos más rápido que el otro? Quiero hacer esto en un contexto sensible al rendimiento.
Albahaca
@Basil - Depende de las circunstancias. Debes hacer algunos horarios para ti y juzgar.
Erik Funkenbusch
44
Math.Abs(d-(int)d) < double.Epsilones más seguro qued == (int)d
Reactgular
3
@MathewFoscarini - Creo que estás confundido. Lo establece en falso, porque el resultado de 16.1 - 6.1 no es un int. El punto era encontrar si un valor dado es un int, no si algo que es aproximadamente un int es un int.
Erik Funkenbusch
1
@MathewFoscarini: Sí, un int es un número sin un valor decimal (o un valor decimal de 0). 16.1-6.1 no crea un valor decimal 0, es un valor distinto de cero muy pequeño causado por las peculiaridades del formato de punto flotante IEEE. No hay forma de saber si se supone que el número tiene un valor decimal o no, por lo que asumir un valor de redondeo es igual de inexacto. El propósito de la pregunta era saber si un número de coma flotante era un número entero, no si era aproximadamente un número entero.
Erik Funkenbusch
21

¿Qué tal esto?

public static bool IsInteger(double number) {
    return number == Math.Truncate(number);
}

El mismo código para decimal.

Mark Byers hizo un buen punto, en realidad: esto puede no ser lo que realmente quieres. Si lo que realmente le importa es si un número redondeado a los dos decimales más cercanos es un número entero , puede hacer esto en su lugar:

public static bool IsNearlyInteger(double number) {
    return Math.Round(number, 2) == Math.Round(number);
}
Dan Tao
fuente
1
quizás actualice su solución y agregue: && number <int.MaxValue && number> int.MinValue
Walter Vehoeven
12

Si bien las soluciones propuestas parecen funcionar para ejemplos simples, hacer esto en general es una mala idea. Es posible que un número no sea exactamente un entero, pero cuando intenta formatearlo, está lo suficientemente cerca de un entero que obtiene 1.000000. Esto puede suceder si hace un cálculo que en teoría debería dar exactamente 1, pero en la práctica da un número muy cercano pero no exactamente igual a uno debido a errores de redondeo.

En su lugar, formatee primero y si su cadena termina en un punto seguido de ceros, luego quítelos. También hay algunos formatos que puede usar que eliminan los ceros finales automáticamente. Esto podría ser lo suficientemente bueno para su propósito.

double d = 1.0002;
Console.WriteLine(d.ToString("0.##"));
d = 1.02;
Console.WriteLine(d.ToString("0.##"));

Salida:

1
1.02
Mark Byers
fuente
@ Mark Suena interesante. ¿Tiene un ejemplo de un formato que elimina los ceros finales?
Jim Geurts
Estoy de acuerdo en que es más seguro y lo que probablemente debería hacer el OP, pero no es una respuesta a la pregunta más estrecha (pero más interesante) de si un valor tiene una parte fraccional o no.
Clifford
3
@Clifford: Por lo general, trato de responder en función de lo que es mejor para resolver el problema de los OP, no en función de lo que dice el título. Los títulos rara vez son una descripción precisa del problema.
Mark Byers
+1 De acuerdo en que intentar probar flotadores o dobles para ver si pueden ser ints es malo debido a errores de redondeo y precisión.
Romain Hippeau
1
Para el uso de dinero, es probable que desee que 1.2 se muestre como 1.20, que no es el caso con la solución sugerida. ¿Ningún arrendatario?
Kjell Rilbe
10
bool IsInteger(double num) {
    if (ceil(num) == num && floor(num) == num)
        return true;
    else
        return false;
}

Problemo solvo.

Editar: Pwned por Mark Rushakoff.

Perrito
fuente
44
o simplementereturn ceil(num) == num && floor(num) == num;
Brian Rasmussen
13
o simplementereturn ceil(num) == floor(num);
gregsdennis
4

La respuesta de Mark Rushakoff puede ser más simple, pero lo siguiente también funciona y puede ser más eficiente ya que no existe una operación de división implícita:

     bool isInteger = (double)((int)f) == f ;

y

     bool isInteger = (decimal)((int)d) == d ;

Si desea una sola expresión para ambos tipos, quizás

     bool isInteger = (double)((int)val) == (double)val ;
Clifford
fuente
4

Si el límite superior e inferior de las Int32materias:

public bool IsInt32(double value)
{
    return  value >= int.MinValue && value <= int.MaxValue && value == (int)value;
}
nawfal
fuente
Primera prueba, luego emitir así arrojaría una excepción además de devolver falso, tal vez actualice su respuesta
Walter Vehoeven
@computadora, sí, buen punto. Con respecto al lanzamiento del elenco, supongo que dependerá de la configuración de su proyecto.
nawfal
3
static bool IsWholeNumber(double x) 
{
    return Math.Abs(x % 1) < double.Epsilon;
}
pomber
fuente
2

Puede usar el formato de cadena para el tipo doble. Aquí hay un ejemplo:

double val = 58.6547;
String.Format("{0:0.##}", val);      
//Output: "58.65"

double val = 58.6;
String.Format("{0:0.##}", val);      
//Output: "58.6"

double val = 58.0;
String.Format("{0:0.##}", val);      
//Output: "58"

Avísame si esto no ayuda.

BALKANGraph
fuente
1
Eso realmente no aborda la cuestión de determinar si un valor no tiene una parte fraccional, que es una pregunta matemática. Sin embargo, probablemente sea lo que el OP necesita dada su nota explicativa.
Clifford
2
Sí, solo quiere formatear el valor doble o decimal sin punto decimal. Gracias ...
BALKANGraph
1
    public static bool isInteger(decimal n)
    {
        return n - (Int64)n == 0;
    }
kent dacalos
fuente
¿Cuál es la diferencia con esta respuesta ?
xskxzr
0

Me enfrenté a una situación similar, pero donde el valor es una cadena. El usuario escribe un valor que se supone que es una cantidad en dólares, por lo que quiero validar que sea numérico y tenga como máximo dos decimales.

Aquí está mi código para devolver verdadero si la cadena "s" representa un número con un máximo de dos decimales y falso en caso contrario. Evita cualquier problema que resulte de la imprecisión de los valores de coma flotante.

try
{
    // must be numeric value
    double d = double.Parse(s);
    // max of two decimal places
    if (s.IndexOf(".") >= 0)
    {
        if (s.Length > s.IndexOf(".") + 3)
            return false;
    }
    return true;
catch
{
    return false;
}

Discuto esto con más detalle en http://progblog10.blogspot.com/2011/04/determining-whether-numeric-value-has.html .

Steve
fuente
44
Esto supone que estás trabajando con una cultura. Por ejemplo, no funcionaría correctamente con culturas que representan decimales como 1,000,00
Jim Geurts
0

El uso de Int. TryParse arrojará estos resultados:

        var shouldBeInt = 3;

        var shouldntBeInt = 3.1415;

        var iDontWantThisToBeInt = 3.000f;

        Console.WriteLine(int.TryParse(shouldBeInt.ToString(), out int parser)); // true

        Console.WriteLine(int.TryParse(shouldntBeInt.ToString(), out parser)); // false

        Console.WriteLine(int.TryParse(iDontWantThisToBeInt.ToString(), out parser)); // true, even if I don't want this to be int

        Console.WriteLine(int.TryParse("3.1415", out  parser)); // false

        Console.WriteLine(int.TryParse("3.0000", out parser)); // false

        Console.WriteLine(int.TryParse("3", out parser)); // true

        Console.ReadKey();
morethanyell
fuente
-2

Quizás no sea la solución más elegante, ¡pero funciona si no eres demasiado exigente!

bool IsInteger(double num) {
    return !num.ToString("0.################").Contains(".");
}
Johan
fuente
8
Esta es una solución terrible
Ospho
-2

Podrías usar el método 'TryParse'.

int.TryParse()

Esto verifica si el valor se puede convertir a un valor entero de número entero. El resultado puede indicar una bandera que se puede utilizar en cualquier otro lugar de su código.

bs3ac
fuente
El argumento para int. TryParse es una cadena, no un doble.
Jim Balter
yourDouble.toString("G17")
BackDoorNoBaby
-2

Prueba esto:

number == Convert.ToInt16(number);
HighTechProgramming15
fuente