¿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.

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)

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;

¿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);
}

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 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.


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 ;

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

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

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.


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


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 .


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();

-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(".");
}

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.


El argumento para int. TryParse es una cadena, no un doble.
Jim Balter

yourDouble.toString("G17")
BackDoorNoBaby

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.