Menu

C# Datentypen: int, double, decimal, char, bool und ihre Wertebereiche

Jeder eingebaute C#-Typ mit Größe, Wertebereich und Literal-Suffix, der Unterschied zwischen Wert- und Referenztypen, warum decimal der Typ für Geld ist, Integer-Überlauf und checked sowie Standardwerte.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

C# ist statisch typisiert: Jede Variable, jedes Feld und jeder Ausdruck hat einen zur Kompilierzeit bekannten Typ. Die Sprache hat eine Reihe eingebauter Typen mit eigenen Schlüsselwörtern (int, double, string...). Jedes Schlüsselwort ist ein Alias für einen Typ im Namespace System, also sind int und System.Int32 derselbe Typ, ebenso string und System.String.

Die eingebauten Typen

Typ.NET-TypGrößeWertebereich oder GenauigkeitLiteral
boolBoolean1 Bytetrue oder falsetrue
byteByte1 Byte0 bis 255200
sbyteSByte1 Byte-128 bis 127-5
shortInt162 Byte-32.768 bis 32.7671200
ushortUInt162 Byte0 bis 65.53560000
intInt324 Byte-2.147.483.648 bis 2.147.483.64742
uintUInt324 Byte0 bis 4.294.967.29542u
longInt648 Byteetwa ±9,2 × 10^1842L
ulongUInt648 Byte0 bis etwa 1,8 × 10^1942ul
floatSingle4 Byteetwa ±3,4 × 10^38, ~6 bis 9 Stellen2.5f
doubleDouble8 Byteetwa ±1,8 × 10^308, ~15 bis 17 Stellen2.5 oder 2.5d
decimalDecimal16 Byteetwa ±7,9 × 10^28, 28 bis 29 Stellen2.5m
charChar2 Byteeine UTF-16-Codeeinheit, U+0000 bis U+FFFF'A'
stringStringReferenzText beliebiger Länge, unveränderlich"hello"
objectObjectReferenzdie Basis jedes Typsnew object()

Ein Ganzzahl-Literal ohne Suffix ist ein int, oder der erste Typ aus uint, long und ulong, der es aufnehmen kann, wenn es für ein int zu groß ist, und ein Literal mit Dezimalpunkt ist ein double. Deshalb kompilieren float f = 2.5; und decimal d = 2.5; nicht: Das Literal ist ein double, und es gibt keine implizite Umwandlung von double in float oder decimal. Hänge das Suffix an: 2.5f, 2.5m.

Jeder numerische Typ stellt seine Grenzen als Konstanten bereit:

Ausgabe:

byte:  0 to 255
short: -32768 to 32767
int:   -2147483648 to 2147483647
long:  -9223372036854775808 to 9223372036854775807
decimal max: 79228162514264337593543950335
double max:  1.798E+308
sizes: int 4, long 8, double 8, char 2
255 161 3000000000

In der Praxis nutzt der meiste Code vier davon: int für Zähler und Indizes, long für IDs und große Summen, double für Messwerte und Mathematik und decimal für Geld.

decimal vs. double

double (und float) speichern Zahlen binär. Die meisten Dezimalbrüche, auch 0.1, haben keine exakte binäre Form, deshalb wird der nächstgelegene darstellbare Wert gespeichert, und kleine Fehler summieren sich. decimal speichert eine Ganzzahl zur Basis 10 plus eine Skalierung, also ist 0.1 exakt 0.1.

Ausgabe:

False
True
0.30000000000000004
0.3
double sum == 1.0: False
decimal sum == 1.0: True
Total: 59.97
Split three ways: 19.99

Nimm decimal für Geld, Preise, Steuern und alles, was ein Mensch von Hand zusammenrechnet und vergleicht. Es behält nachgestellte Nullen aus seinen Eingaben (1.50m wird als 1.50 ausgegeben) und hat 28 bis 29 signifikante Stellen.

Nimm double für physikalische Messwerte, Statistik, Geometrie und Spiele. Es ist um ein Mehrfaches schneller, braucht halb so viel Speicher, und sein Wertebereich ist riesig. Vergleiche zwei berechnete Doubles nie mit ==; vergleiche die Differenz mit einer Toleranz: Math.Abs(x - y) < 1e-9.

decimal ist keine Magie: 1m / 3m ist trotzdem 0.3333333333333333333333333333, und double-Arithmetik wirft bei Division durch null keine Exception (sie ergibt Unendlich, oder NaN bei 0.0 / 0.0), während Division durch null bei decimal und Ganzzahlen eine DivideByZeroException wirft.

Integer-Überlauf und checked

Ganzzahlarithmetik, die den Wertebereich des Typs überschreitet, läuft standardmäßig über und springt stillschweigend um. Der Compiler erkennt Überläufe in konstanten Ausdrücken, aber nicht, wenn die Werte aus Variablen kommen:

Ausgabe:

-2147483648
OverflowException
-1294967296
2147483648

checked(...) oder ein Block checked { ... } macht aus einem Überlauf eine OverflowException. Du kannst das auch für das ganze Projekt mit <CheckForOverflowUnderflow>true</CheckForOverflowUnderflow> einschalten. int x = int.MaxValue + 1; mit Konstanten zu schreiben ist ein Kompilierfehler (CS0220), weil der Compiler den Überlauf sieht.

Die letzte Zeile zeigt die Lösung für die meisten Überlauffehler: vor der Arithmetik in einen breiteren Typ umwandeln. (long)(max + 1) wäre falsch, denn die Addition ist bereits übergelaufen, wenn der Cast ausgeführt wird.

Werttypen und Referenztypen

Jeder C#-Typ ist entweder ein Werttyp oder ein Referenztyp, und dieser Unterschied entscheidet, was eine Zuweisung tut.

  • Werttypen: alle numerischen Typen, bool, char, DateTime, Enums und jedes struct. Die Variable enthält die Daten; eine Zuweisung kopiert sie.
  • Referenztypen: string, Arrays, object und jede class. Die Variable enthält eine Referenz auf ein Objekt; eine Zuweisung kopiert die Referenz, sodass beide Variablen dasselbe Objekt sehen.

Ausgabe:

a=10 b=99
first[0]=99
s1=cat s2=cats

string ist ein Referenztyp, der sich in der Praxis wie ein Wert verhält: Er ist unveränderlich, und == vergleicht den Text, nicht die Referenzen. Mehr über benutzerdefinierte Werttypen unter Structs.

char und bool

Ein char ist eine UTF-16-Codeeinheit und wird in einfachen Anführungszeichen geschrieben. Darunter ist es ein numerischer Typ, es lässt sich also in int umwandeln und unterstützt Arithmetik:

Ausgabe:

66
C
True
True
7
b
True

bool hat genau zwei Werte und lässt sich nicht in Zahlen umwandeln oder aus ihnen erzeugen: if (count) ist ein Kompilierfehler, kein Test auf ungleich null. Schreibe if (count != 0). Zeichen außerhalb der Basic Multilingual Plane, etwa die meisten Emojis, belegen zwei char-Werte (ein Surrogatpaar), daher zählt die Length eines string UTF-16-Einheiten, keine sichtbaren Zeichen.

Standardwerte

Felder, Array-Elemente und default(T) bekommen den Standardwert des Typs: null für Zahlen, false für bool, '\0' für char und null für Referenztypen. Lokale Variablen bekommen keinen Standardwert und müssen vor der Verwendung zugewiesen werden.

Ausgabe:

0
0
False
True
0001-01-01
0,0,0

Seit C# 7.1 kannst du das Literal default ohne den Typ schreiben, wenn der Compiler ihn ableiten kann (int x = default;). Ein Werttyp kann nicht null sein, dafür gibt es int? (einen Nullable-Werttyp); siehe Nullable-Typen.

Ganzzahlen in Zeigergröße und beliebig große Ganzzahlen

Für Sonderfälle gibt es zwei weitere numerische Typen. nint und nuint (C# 9) sind Ganzzahlen in Zeigergröße, je nach Plattform 32 oder 64 Bit, und werden in Interop- und Low-Level-Code verwendet. System.Numerics.BigInteger nimmt Ganzzahlen beliebiger Größe auf, auf Kosten der Geschwindigkeit:

using System.Numerics;
BigInteger factorial = 1;
for (int i = 2; i <= 30; i++) factorial *= i;
// 265252859812191058636308480000000

Häufig gestellte Fragen

Welche Datentypen gibt es in C#?

Die eingebauten Typen sind die Ganzzahlen (sbyte, byte, short, ushort, int, uint, long, ulong), die Gleitkommatypen float und double, decimal für exakte Dezimalarithmetik, char für ein UTF-16-Zeichen, bool sowie die Referenztypen string und object. Jedes Schlüsselwort ist ein Alias für einen .NET-Typ: int ist System.Int32, string ist System.String.

Soll ich in C# decimal oder double für Geld verwenden?

Nimm decimal. Ein double speichert Binärbrüche, deshalb lassen sich Werte wie 0.1 nicht exakt darstellen und Summen driften ab (0.1 + 0.2 == 0.3 ist false). Ein decimal speichert Ziffern zur Basis 10, behält 28 bis 29 signifikante Stellen und liefert die Ergebnisse, die Buchhalter erwarten. double ist schneller und hat einen weit größeren Wertebereich, nimm es also für Wissenschaft, Grafik und Messwerte.

Was ist der Maximalwert von int in C#?

int.MaxValue ist 2.147.483.647 und int.MinValue ist -2.147.483.648 (eine vorzeichenbehaftete 32-Bit-Ganzzahl). Wird zu int.MaxValue 1 addiert, springt der Wert stillschweigend auf int.MinValue um, außer der Code läuft in einem checked-Kontext, wo eine OverflowException geworfen wird. Für größere Werte nimm long (bis etwa 9,2 Trillionen).

Was ist der Unterschied zwischen float und double in C#?

float hat 32 Bit mit etwa 6 bis 9 signifikanten Stellen; double hat 64 Bit mit etwa 15 bis 17. Gleitkomma-Literale sind standardmäßig double, ein float braucht also das Suffix f: float speed = 2.5f;. Nimm double, außer Speicher oder eine API (etwa Vector3 in Unity) verlangt float.

Was ist der Unterschied zwischen Werttypen und Referenztypen in C#?

Eine Variable eines Werttyps (int, double, bool, DateTime, jedes struct) enthält die Daten selbst, und eine Zuweisung kopiert sie. Eine Variable eines Referenztyps (string, Arrays, jede class) enthält eine Referenz auf ein Objekt im Heap, und eine Zuweisung kopiert die Referenz, sodass zwei Variablen auf dasselbe Objekt zeigen können.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S