Jak ustalić, czy liczba dziesiętna / podwójna jest liczbą całkowitą?


225

Jak sprawdzić, czy wartość dziesiętna czy podwójna jest liczbą całkowitą?

Na przykład:

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

lub

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

Powodem, dla którego chciałbym to wiedzieć, jest to, że mogę programowo określić, czy chcę wyprowadzić wartość za pomocą .ToString("N0")lub .ToString("N2"). Jeśli nie ma wartości dziesiętnej, nie chcę tego pokazywać.

Odpowiedzi:


410

W przypadku liczb zmiennoprzecinkowych n % 1 == 0 jest to zazwyczaj sposób sprawdzenia, czy coś znajduje się poza przecinkiem dziesiętnym.

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

Wynik:

False
True

Aktualizacja: Jak wspomniano poniżej @Adrian Lopez, porównanie z małą wartościąepsilonodrzuci błędne obliczenia obliczeń zmiennoprzecinkowych. Ponieważ pytanie dotyczydoublewartości, poniżej będzie bardziej zmiennoprzecinkowa odpowiedź na obliczenia :

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

96
Działa to, gdy liczba zaczyna się od liczby całkowitej, ale niekoniecznie, gdy liczba jest wynikiem obliczeń zmiennoprzecinkowych. A może coś w stylu „(d% 1) <epsilon”, gdzie epsion ma niewielką wartość?
Adrian Lopez

9
Szkoda, że ​​najlepszą odpowiedzią w tym wątku jest komentarz, a nie odpowiedź zaakceptowana. Niezły Adrian.
starskythehutch

13
Myślę też, że powyższy komentarz Adriana jest najlepszą odpowiedzią. Aby umieścić swoją radę w formalnym kodzie C #: if (Math.Abs ​​(n% 1) <Double.Epsilon) {// Zrób coś, jeśli n jest liczbą całkowitą}.
Ruben Ramirez Padron

7
IMHO, operator modułu i liczby zmiennoprzecinkowe po prostu nie mieszają się w żaden sposób użyteczny. Sugerowany kod jest niezwykle mylący, biorąc pod uwagę liczbę używanych naciśnięć klawiszy i będzie działać prawie nigdzie poza językami .NET. Założę się, że jest on również znacznie wolniejszy niż właściwy sposób (który w ogóle nie używa podziału). Prawidłowym sposobem na to jest użycie Math.Abs(d-(int)d) < double.Epsilon. Jakbyśmy wszyscy powinni byli się uczyć w pierwszym tygodniu naszej pierwszej klasy programowania na studiach.
krowe2

9
W rzeczywistości, ponieważ pytanie jest postawione, ta odpowiedź jest poprawna, a komentarze błędne. OP nie chce wiedzieć, czy liczba podwójna jest liczbą całkowitą dla celów matematycznych, ale raczej jak ją wyświetlić. Tylko dokładne wartości całkowite powinny być wyświetlane bez kropki dziesiętnej. Również komentarz o tym, że mod nie jest użyteczny w przypadku liczb zmiennoprzecinkowych i nie działa poza .NET, nie jest dobrze poinformowany. I (int)dto katastrofa, która spowoduje wyjątek dla większości podwójnych wartości.
Jim Balter

49

Można to zrobić na wiele sposobów. Na przykład:

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

Możesz także użyć modulo.

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

Czy jeden z nich byłby szybszy od drugiego? Chcę to zrobić w kontekście wrażliwym na wydajność.
Basil

@Basil - Zależy od okoliczności. Powinieneś zrobić dla siebie trochę czasu i osądzić.
Erik Funkenbusch,

4
Math.Abs(d-(int)d) < double.Epsilonjest bezpieczniejszy niżd == (int)d
Reactgular

3
@MathewFoscarini - Myślę, że jesteś zdezorientowany. Ustawia go na false, ponieważ wynik 16.1 - 6.1 nie jest liczbą całkowitą. Chodziło o to, czy dana wartość jest liczbą całkowitą, a nie czy coś, co jest w przybliżeniu liczbą całkowitą, jest liczbą całkowitą.
Erik Funkenbusch

1
@MathewFoscarini - Tak, int to liczba bez wartości dziesiętnej (lub dziesiętnej 0). 16.1-6.1 nie tworzy wartości dziesiętnej 0, jest to bardzo mała niezerowa wartość, która jest spowodowana dziwactwami zmiennoprzecinkowymi w formacie IEEE. Nie ma sposobu, aby dowiedzieć się, czy liczba ma mieć wartość dziesiętną, czy też nie, więc przyjęcie wartości zaokrąglenia jest równie niedokładne. Celem tego pytania było sprawdzenie, czy liczba zmiennoprzecinkowa jest liczbą całkowitą, a nie czy w przybliżeniu jest liczbą całkowitą.
Erik Funkenbusch

21

Co powiesz na to?

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

Ten sam kod dla decimal .

Mark Byers miał rację: właściwie to może nie być to, czego naprawdę chcesz. Jeśli naprawdę zależy Ci na tym, czy liczba zaokrąglona do najbliższych dwóch miejsc po przecinku jest liczbą całkowitą , możesz to zrobić w zamian:

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

1
może zaktualizuj swoje rozwiązanie i dodaj: && number <int.MaxValue && number> int.MinValue
Walter Vehoeven

12

Chociaż proponowane rozwiązania wydają się sprawdzać w przypadku prostych przykładów, robienie tego ogólnie jest złym pomysłem. Liczba może nie być dokładnie liczbą całkowitą, ale podczas próby jej sformatowania jest wystarczająco blisko liczby całkowitej, którą otrzymujesz1.000000 . Może się to zdarzyć, jeśli wykonasz obliczenia, które teoretycznie powinny dać dokładnie 1, ale w praktyce daje liczbę bardzo zbliżoną, ale nie dokładnie taką samą, z powodu błędów zaokrąglania.

Zamiast tego sformatuj go najpierw, a jeśli ciąg zakończy się kropką, po której następuje zero, usuń je. Istnieje również kilka formatów, z których można automatycznie korzystać po końcowych zerach. To może być wystarczająco dobre dla twojego celu.

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

Wynik:

1
1.02

@Mark Brzmi interesująco. Czy masz przykład formatu, który usuwa końcowe zera?
Jim Geurts

Zgadzam się, że jest to bezpieczniejsze i co prawdopodobnie powinien zrobić PO, ale nie jest to odpowiedź na węższe (ale bardziej interesujące) pytanie, czy wartość ma ułamkową część, czy nie.
Clifford

3
@Clifford: Zwykle staram się odpowiadać na podstawie tego, co najlepiej rozwiązać problem PO, a nie na podstawie tego, co mówi tytuł. Tytuły rzadko są dokładnym opisem problemu.
Mark Byers

+1 Zgadzam się, że próba testowania liczb zmiennoprzecinkowych lub podwójnych w celu sprawdzenia, czy mogą to być liczby int, jest zła z powodu błędów zaokrąglania i precyzji.
Romain Hippeau

1
W przypadku korzystania z pieniędzy prawdopodobnie chcesz, aby wartość 1,2 była wyświetlana jako 1,20, co nie ma miejsca w przypadku sugerowanego rozwiązania. Jacyś chętni?
Kjell Rilbe

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

Problemo solvo.

Edycja: Pwned Mark Rushakoff.


4
lub po prostureturn ceil(num) == num && floor(num) == num;
Brian Rasmussen

13
lub po prostureturn ceil(num) == floor(num);
gregsdennis

4

Odpowiedź Marka Rushakoffa może być prostsza, ale poniższe również działają i mogą być bardziej wydajne, ponieważ nie istnieje niejawna operacja podziału:

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

i

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

Być może potrzebujesz jednego wyrażenia dla obu typów

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

4

Jeśli górna i dolna granica Int32spraw:

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

Pierwszy test, a następnie rzut w ten sposób
rzuciłby

@ komputer, tak, dobra uwaga. Jeśli chodzi o rzucanie na obsadę, myślę, że będzie to zależeć od ustawień twojego projektu.
nawfal

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

2

Możesz użyć formatowania ciągu dla typu podwójnego. Oto przykład:

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"

Daj mi znać, jeśli to nie pomoże.


1
To tak naprawdę nie rozwiązuje problemu ustalenia, czy wartość nie ma części ułamkowej, co jest pytaniem matematycznym. Jest to jednak prawdopodobnie to, czego potrzebuje OP, biorąc pod uwagę jego uzasadnienie.
Clifford

2
Tak, chce po prostu sformatować wartość podwójną lub dziesiętną bez kropki dziesiętnej. Dziękuję ...
BALKANGraph


0

Napotkałem podobną sytuację, ale gdzie wartość jest łańcuchem. Użytkownik wpisuje wartość, która powinna być kwotą w dolarach, dlatego chcę sprawdzić, czy jest ona liczbowa i ma najwyżej dwa miejsca po przecinku.

Oto mój kod do zwrócenia true, jeśli ciąg „s” reprezentuje liczbę z co najwyżej dwoma miejscami dziesiętnymi, a false w przeciwnym razie. Pozwala to uniknąć problemów wynikających z niedokładności wartości zmiennoprzecinkowych.

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

Omawiam to bardziej szczegółowo na stronie http://progblog10.blogspot.com/2011/04/determining-whether-numeric-value-has.html .


4
Zakłada się, że pracujesz z jedną kulturą. Na przykład nie działałoby to poprawnie w przypadku kultur reprezentujących miejsca dziesiętne, takie jak 1.000,00
Jim Geurts

0

Użycie int.TryParse przyniesie następujące wyniki:

        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

Być może nie jest to najbardziej eleganckie rozwiązanie, ale działa, jeśli nie jesteś zbyt wybredny!

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

8
To okropne rozwiązanie
Ospho,

-2

Możesz użyć metody „TryParse”.

int.TryParse()

To sprawdza, czy wartość można przekonwertować na wartość całkowitą liczby całkowitej. Wynik może następnie wskazywać flagę, której można użyć w innym miejscu w kodzie.


Argumentem int.TryParse jest ciąg, a nie podwójny.
Jim Balter

yourDouble.toString("G17")
BackDoorNoBaby

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.