Wie kann man feststellen, ob eine Dezimalzahl / ein Doppel eine ganze Zahl ist?


225

Wie kann ich feststellen, ob ein Dezimal- oder Doppelwert eine Ganzzahl ist?

Beispielsweise:

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

oder

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

Der Grund, warum ich dies wissen möchte, ist, dass ich programmgesteuert bestimmen kann, ob ich den Wert mit .ToString("N0")oder ausgeben möchte .ToString("N2"). Wenn es keinen Dezimalpunktwert gibt, möchte ich das nicht zeigen.

Antworten:


409

Bei Gleitkommazahlen n % 1 == 0wird normalerweise überprüft, ob der Dezimalpunkt überschritten wird.

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

Ausgabe:

False
True

Update: Wie unten bei @Adrian Lopez erwähnt, werden beim Vergleich mit einem kleinen WertepsilonGleitkomma-Berechnungsfehlberechnungen verworfen. Da es sich bei der Frage umdoubleWerte handelt, finden Sie im Folgenden eineAntwort mitmehr Gleitkomma-Berechnungsnachweisen :

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

96
Dies funktioniert, wenn die Zahl als ganze Zahl beginnt, aber nicht unbedingt, wenn die Zahl das Ergebnis einer Gleitkommaberechnung ist. Wie wäre es mit so etwas wie "(d% 1) <epsilon", wo epsion einen kleinen Wert hat?
Adrian Lopez

9
Es ist eine Schande, dass die beste Antwort in diesem Thread eher ein Kommentar als die akzeptierte Antwort ist. Netter Adrian.
Starskythehutch

13
Ich denke auch, dass Adrians Kommentar oben die beste Antwort ist. Um seinen Rat in formalen C # -Code zu setzen: if (Math.Abs ​​(n% 1) <Double.Epsilon) {// Mach etwas, wenn n eine ganze Zahl ist}.
Ruben Ramirez Padron

7
IMHO, der Moduloperator und Gleitkommazahlen mischen sich einfach in keiner Weise nützlich. Der vorgeschlagene Code ist angesichts der Anzahl der verwendeten Tastenanschläge unglaublich verwirrend und funktioniert außerhalb von .NET-Sprachen fast nirgendwo. Ich würde wetten, dass es auch viel langsamer ist als der richtige Weg (der überhaupt keine Division verwendet). Der richtige Weg, dies zu tun, ist zu verwenden Math.Abs(d-(int)d) < double.Epsilon. Wie wir alle in der ersten Woche unserer ersten Programmierklasse am College hätten lernen sollen.
Krowe2

9
Wie die Frage besagt, ist diese Antwort tatsächlich richtig und die Kommentare sind falsch. Das OP möchte nicht wissen, ob ein Double für mathematische Zwecke eine Ganzzahl ist, sondern wie es angezeigt wird. Es sollten nur exakte Ganzzahlwerte ohne Dezimalpunkt angezeigt werden. Auch der Kommentar darüber, dass Mod mit Gleitkomma nicht nützlich ist und nicht außerhalb von .NET funktioniert, ist nicht gut informiert. Und (int)dist eine Katastrophe, die für die meisten Doppelwerte eine Ausnahme auslöst.
Jim Balter

49

Es gibt verschiedene Möglichkeiten, dies zu tun. Beispielsweise:

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

Sie können auch Modulo verwenden.

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

Wäre einer davon schneller als der andere? Ich möchte dies in einem leistungssensiblen Kontext tun.
Basil

@Basil - Hängt von den Umständen ab. Sie sollten einige Zeitangaben für sich selbst machen und beurteilen.
Erik Funkenbusch

4
Math.Abs(d-(int)d) < double.Epsilonist sicherer alsd == (int)d
Reactgular

3
@ MathewFoscarini - Ich denke du bist verwirrt. Es setzt es auf false, da das Ergebnis von 16.1 - 6.1 kein int ist. Es ging darum herauszufinden, ob ein gegebener Wert ein int ist, und nicht, ob etwas, das ungefähr ein int ist, ein int ist.
Erik Funkenbusch

1
@MathewFoscarini - Ja, ein int ist eine Zahl ohne Dezimalwert (oder einen Dezimalwert von 0). 16.1-6.1 erstellt keinen 0-Dezimalwert, sondern einen sehr kleinen Wert ungleich Null, der durch Macken im IEEE-Gleitkommaformat verursacht wird. Es gibt keine Möglichkeit zu wissen, ob die Zahl einen Dezimalwert haben soll oder nicht. Daher ist die Annahme eines Rundungswerts genauso ungenau. Der Zweck der Frage bestand darin zu wissen, ob eine Gleitkommazahl eine Ganzzahl ist, nicht, ob es sich ungefähr um eine Ganzzahl handelt.
Erik Funkenbusch

21

Wie wäre es damit?

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

Gleicher Code für decimal.

Mark Byers machte tatsächlich einen guten Punkt: Dies ist möglicherweise nicht das, was Sie wirklich wollen. Wenn es Ihnen wirklich wichtig ist, ob eine auf die nächsten zwei Dezimalstellen gerundete Zahl eine Ganzzahl ist , können Sie stattdessen Folgendes tun:

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

1
Vielleicht aktualisieren Sie Ihre Lösung und fügen hinzu: && Nummer <int.MaxValue && Nummer> int.MinValue
Walter Vehoeven

12

Während die vorgeschlagenen Lösungen für einfache Beispiele zu funktionieren scheinen, ist dies im Allgemeinen eine schlechte Idee. Eine Zahl ist möglicherweise nicht genau eine Ganzzahl, aber wenn Sie versuchen, sie zu formatieren, liegt sie nahe genug an einer Ganzzahl, die Sie erhalten 1.000000. Dies kann passieren, wenn Sie eine Berechnung durchführen, die theoretisch genau 1 ergeben sollte, in der Praxis jedoch aufgrund von Rundungsfehlern eine Zahl ergibt, die sehr nahe an einer liegt, aber nicht genau gleich eins ist.

Formatieren Sie es stattdessen zuerst. Wenn Ihre Zeichenfolge in einem Punkt gefolgt von Nullen endet, entfernen Sie sie. Es gibt auch einige Formate, in denen Sie diese nachgestellten Nullen automatisch verwenden können. Dies könnte für Ihren Zweck gut genug sein.

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

Ausgabe:

1
1.02

@ Mark Klingt interessant. Haben Sie ein Beispiel für ein Format, bei dem nachgestellte Nullen entfernt werden?
Jim Geurts

Ich stimme zu, dass es sicherer ist und was das OP wahrscheinlich tun sollte, aber es ist keine Antwort auf die engere (aber interessantere) Frage, ob ein Wert einen Bruchteil hat oder nicht.
Clifford

3
@ Clifford: Normalerweise versuche ich zu antworten, basierend darauf, was am besten ist, um das OP-Problem zu lösen, und nicht basierend darauf, was der Titel sagt. Titel sind selten eine genaue Beschreibung des Problems.
Mark Byers

+1 Stimmen Sie zu, dass der Versuch, Floats oder Doubles zu testen, um festzustellen, ob es sich um Ints handelt, aufgrund von Rundungs- und Präzisionsfehlern schlecht ist.
Romain Hippeau

1
Für die Verwendung von Geld möchten Sie wahrscheinlich, dass 1.2 als 1.20 angezeigt wird, was bei der vorgeschlagenen Lösung nicht der Fall ist. Irgendwelche Abnehmer?
Kjell Rilbe

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

Problemo solvo.

Edit: Pwned von Mark Rushakoff.


4
oder einfachreturn ceil(num) == num && floor(num) == num;
Brian Rasmussen

13
oder einfachreturn ceil(num) == floor(num);
Gregsdennis

4

Mark Rushakoffs Antwort mag einfacher sein, aber die folgenden funktionieren auch und sind möglicherweise effizienter, da es keine implizite Teilungsoperation gibt:

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

und

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

Wenn Sie vielleicht einen einzigen Ausdruck für beide Typen wünschen

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

4

Wenn Ober- und Untergrenze von Int32Angelegenheiten:

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

Erst testen, dann so wirken, es würde eine andere Ausnahme als false zurückgeben, vielleicht aktualisieren Sie Ihre Antwort
Walter Vehoeven

@ Computer, ja guter Punkt. Ich denke, es hängt von Ihrer Projekteinstellung ab.
Nawfal

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

2

Sie können die Zeichenfolgenformatierung für den Doppeltyp verwenden. Hier ist ein Beispiel:

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"

Lassen Sie mich wissen, wenn dies nicht hilft.


1
Damit geht es nicht wirklich um die Frage, ob ein Wert keinen Bruchteil hat, was eine mathematische Frage ist. Es ist jedoch wahrscheinlich das, was das OP angesichts seiner Erläuterung benötigt.
Clifford

2
Ja, er möchte nur einen Doppel- oder Dezimalwert ohne Dezimalpunkt formatieren. Vielen Dank ...
BALKANGraph


0

Ich sah mich einer ähnlichen Situation gegenüber, aber wo der Wert eine Zeichenfolge ist. Der Benutzer gibt einen Wert ein, der ein Dollarbetrag sein soll. Daher möchte ich überprüfen, ob er numerisch ist und höchstens zwei Dezimalstellen hat.

Hier ist mein Code, der true zurückgibt, wenn die Zeichenfolge "s" eine Zahl mit höchstens zwei Dezimalstellen darstellt, andernfalls false. Es werden Probleme vermieden, die sich aus der Ungenauigkeit von Gleitkommawerten ergeben würden.

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

Ich diskutiere dies ausführlicher unter http://progblog10.blogspot.com/2011/04/determining-whether-numeric-value-has.html .


4
Dies setzt voraus, dass Sie mit einer Kultur arbeiten. Zum Beispiel würde es nicht richtig mit Kulturen funktionieren, die Dezimalstellen wie 1.000,00 darstellen
Jim Geurts

0

Die Verwendung von int.TryParse führt zu folgenden Ergebnissen:

        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

Vielleicht nicht die eleganteste Lösung, aber es funktioniert, wenn Sie nicht zu wählerisch sind!

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

8
Dies ist eine schreckliche Lösung
Ospho

-2

Sie können die Methode 'TryParse' verwenden.

int.TryParse()

Dies prüft, ob der Wert in einen ganzzahligen Ganzzahlwert konvertiert werden kann. Das Ergebnis kann dann ein Flag anzeigen, das an anderer Stelle in Ihrem Code verwendet werden kann.


Das Argument für int.TryParse ist eine Zeichenfolge, keine doppelte.
Jim Balter

yourDouble.toString("G17")
BackDoorNoBaby

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.