Como determinar se um decimal / duplo é um número inteiro?


225

Como saber se um valor decimal ou duplo é um número inteiro?

Por exemplo:

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

ou

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

A razão pela qual gostaria de saber isso é para que eu possa determinar programaticamente se quero gerar o valor usando .ToString("N0")or .ToString("N2"). Se não houver valor de ponto decimal, não quero mostrar isso.

Respostas:


410

Para números de ponto flutuante, n % 1 == 0normalmente é a maneira de verificar se há algo além do ponto decimal.

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

Resultado:

False
True

Atualização: Como o @Adrian Lopez mencionou abaixo, a comparação com um valor pequenoepsilondescartará erros de cálculo da computação em ponto flutuante. Como a pergunta é sobredoublevalores, abaixo será apresentada umaresposta mais à prova de cálculo de ponto flutuante :

Math.Abs(d % 1) <= (Double.Epsilon * 100)

96
Isso funciona quando o número começa como um número inteiro, mas não necessariamente quando o número é o resultado de algum cálculo de ponto flutuante. Que tal algo como "(d% 1) <epsilon", onde epsion é um valor pequeno?
Adrian Lopez

9
É uma pena que a melhor resposta neste tópico seja um comentário, e não a resposta aceita. Agradável Adrian.
starskythehutch

13
Eu também acho que o comentário de Adrian acima é a melhor resposta. Para colocar seu conselho no código C # formal: if (Math.Abs ​​(n% 1) <Double.Epsilon) {// Faça algo se n for inteiro}.
Ruben Ramirez Padron

7
IMHO, o operador de módulo e os números de ponto flutuante simplesmente não se misturam de maneira alguma útil. O código sugerido é incrivelmente confuso, dado o número de pressionamentos de teclas usados ​​e não funcionará quase em nenhum lugar fora das linguagens .NET. Eu apostaria que também é muito mais lento que o caminho correto (que não usa nenhuma divisão). A maneira correta de fazer isso é usar Math.Abs(d-(int)d) < double.Epsilon. Como todos nós deveríamos ter aprendido na primeira semana de nossa primeira aula de programação na faculdade.
krowe2

9
Na verdade, como a pergunta é afirmada, esta resposta está correta e os comentários estão errados. O OP não quer saber se um duplo é um número inteiro para fins matemáticos, mas sim como exibi-lo. Somente valores inteiros exatos devem ser exibidos sem um ponto decimal. Além disso, o comentário sobre o mod não ser útil com ponto flutuante e não funcionar fora do .NET não é bem informado. E (int)dé um desastre que lançará uma exceção para a maioria dos valores duplos.
Jim Balter

49

Existem várias maneiras de fazer isso. Por exemplo:

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

Você também pode usar o módulo.

double d = 5.0;
bool isInt = d % 1 == 0;

Um deles seria mais rápido que o outro? Estou querendo fazer isso em um contexto sensível ao desempenho.
Basil

@ Basil - Depende das circunstâncias. Você deve fazer alguns horários para si mesmo e julgar.
Erik Funkenbusch 04/12/13

4
Math.Abs(d-(int)d) < double.Epsiloné mais seguro do qued == (int)d
#

3
@ MatthewFoscarini - Eu acho que você está confuso. Ele define como false, porque o resultado de 16.1 - 6.1 não é um int. O objetivo era descobrir se um determinado valor é um int, não se algo que é aproximadamente um int é um int.
Erik Funkenbusch 31/01

1
@MathewFoscarini - Sim, um int é um número sem um valor decimal (ou um valor decimal de 0). 16.1-6.1 não cria um valor decimal 0, é um valor diferente de zero muito pequeno causado pelas peculiaridades do formato de ponto flutuante IEEE. Não há como saber se o número tem um valor decimal ou não, portanto, supondo que um valor de arredondamento seja impreciso. O objetivo da pergunta era saber se um número de ponto flutuante era um número inteiro, não se era aproximadamente um número inteiro.
Erik Funkenbusch

21

Que tal agora?

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

O mesmo código para decimal.

Mark Byers fez um bom argumento, na verdade: isso pode não ser o que você realmente deseja. Se você realmente gosta de saber se um número arredondado para as duas casas decimais mais próximas é um número inteiro , você pode fazer isso:

public static bool IsNearlyInteger(double number) {
    return Math.Round(number, 2) == Math.Round(number);
}

1
talvez atualizar sua solução e adicionar: && número <int.MaxValue número &&> int.MinValue
Walter Vehoeven

12

Embora as soluções propostas pareçam funcionar com exemplos simples, fazer isso em geral é uma má idéia. Um número pode não ser exatamente um número inteiro, mas quando você tenta formatá-lo, é próximo o suficiente de um número inteiro que você obtém 1.000000. Isso pode acontecer se você fizer um cálculo que, em teoria, deve fornecer exatamente 1, mas, na prática, fornece um número muito próximo, mas não exatamente igual a um, devido a erros de arredondamento.

Em vez disso, formate-o primeiro e, se sua string terminar em um período seguido por zeros, retire-os. Também existem alguns formatos que você pode usar para remover zeros à direita automaticamente. Isso pode ser bom o suficiente para o seu propósito.

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

Resultado:

1
1.02

@ Mark Parece interessante. Você tem um exemplo de um formato que retira zeros à direita?
Jim Geurts

Concordo que é mais seguro e o que o OP provavelmente deve fazer, mas não é uma resposta para a pergunta mais restrita (mas mais interessante) sobre se um valor tem uma parte fracionária ou não.
Clifford

3
@Clifford: Normalmente, tento responder com base no que é melhor para resolver o problema dos OPs, não com base no que o título diz. Os títulos raramente são uma descrição precisa do problema.
Mark Byers

+1 Concordo que tentar testar flutuadores ou dobras para ver se eles podem ser ints é ruim devido a erros de arredondamento e precisão.
Romain Hippeau

1
Para uso de dinheiro, você provavelmente deseja que o 1.2 seja exibido como 1.20, o que não é o caso da solução sugerida. Algum comprador?
Kjell Rilbe

10
bool IsInteger(double num) {
    if (ceil(num) == num && floor(num) == num)
        return true;
    else
        return false;
}

Problemo solvo.

Edit: Pwned por Mark Rushakoff.


4
ou apenasreturn ceil(num) == num && floor(num) == num;
Brian Rasmussen

13
ou apenasreturn ceil(num) == floor(num);
gregsdennis

4

A resposta de Mark Rushakoff pode ser mais simples, mas o seguinte também funciona e pode ser mais eficiente, pois não há operação implícita de divisão:

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

e

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

Se você deseja uma única expressão para os dois tipos, talvez

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

4

Se o limite superior e inferior da Int32questão:

public bool IsInt32(double value)
{
    return  value >= int.MinValue && value <= int.MaxValue && value == (int)value;
}

Primeiro teste, em seguida, lançar como esta seria lançar uma exceção que não seja falsa retorno, talvez atualizar a sua resposta
Walter Vehoeven

@ computador, sim, bom ponto. Em relação a jogar no elenco, acho que isso depende da configuração do seu projeto.
Nawfal 20/08/19

3
static bool IsWholeNumber(double x) 
{
    return Math.Abs(x % 1) < double.Epsilon;
}

2

Você pode usar a formatação de seqüência de caracteres para o tipo duplo. Aqui está um exemplo:

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"

Deixe-me saber se isso não ajuda.


1
Isso realmente não trata da questão de determinar se um valor não tem parte fracionária, que é uma questão matemática. No entanto, é provavelmente o que o OP precisa, dada sua nota explicativa.
Clifford

2
Sim, ele quer apenas formatar o valor duplo ou decimal sem ponto decimal. Agradeço o seu
contato


0

Eu enfrentei uma situação semelhante, mas onde o valor é uma string. O usuário digita um valor que deveria ser uma quantia em dólar, então quero validar que é numérico e tem no máximo duas casas decimais.

Aqui está o meu código para retornar true se a string "s" representar um numérico com no máximo duas casas decimais e false caso contrário. Evita quaisquer problemas que resultariam da imprecisão dos valores de ponto flutuante.

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 isso com mais detalhes em http://progblog10.blogspot.com/2011/04/determining-whether-numeric-value-has.html .


4
Isso pressupõe que você esteja trabalhando com uma cultura. Por exemplo, ele não funcionaria corretamente com culturas que representam decimais como 1.000,00
Jim Geurts

0

Usar int.TryParse produzirá estes 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

Talvez não seja a solução mais elegante, mas funciona se você não for muito exigente!

bool IsInteger(double num) {
    return !num.ToString("0.################").Contains(".");
}

8
Esta é uma solução terrível
Ospho

-2

Você pode usar o método 'TryParse'.

int.TryParse()

Isso verifica se o valor pode ser convertido em um número inteiro inteiro. O resultado pode indicar um sinalizador que pode ser usado em outro lugar no seu código.


O argumento para int.TryParse é uma sequência, não um duplo.
Jim Balter

yourDouble.toString("G17")
precisa saber é o seguinte

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.