# Arbeiten mit .NET: Grundlagen: Datentypen/ Dezimalzahlen

Dieses Kapitel enthält Abschnitte für mehrere Varianten, die teilweise geprüft oder ergänzt werden müssen. Problemstellen: Code für C++ Wenn du einen Teil geprüft oder geändert hast, kannst du den Hinweis darauf aus der Liste der Teile entfernen. Wenn der letzte Teil geprüft worden ist, kann die gesamte {{Vorlage:Fehlender Teil}} entfernt werden.
In diesem Kapitel erhalten Sie genauere Erläuterungen zu denjenigen einfachen Datentypen, mit denen Dezimalzahlen verarbeitet werden.

## Überblick

Die folgende Tabelle nennt zu jedem Datentyp, der für eine Dezimalzahl steht, den Standardwert sowie das mögliche Minimum und Maximum. Als Postfix oder Suffix wird ein Zeichen genannt, das an den Wert angehängt wird, um den Typ zu kennzeichnen. Alle diese Typen gehören zu den vorgeschriebenen Standardtypen (den "CLS-kompatiblen") und sind deshalb fett gedruckt.
| Klassenname | CLS | Standard | Postfix | Minimum | Maximum | Länge | Genauigkeit | unendlich |
| Single | ja | 0.0 | F in C# F in VB | –3,402823E38 = – 3,402823 * 10$^{38}$ | +3,402823E38 = 3,402823 * 10$^{38}$ | 39 Stellen | 7 Stellen | definiert |
| Double | ja | 0.0 | (kein) | –1,79769313486232E308 ≈ – 1,797693 * 10$^{308}$ | +1,79769313486232E308 ≈ 1,797693 * 10$^{308}$ | 309 Stellen | 15–16 Stellen | definiert |
| Decimal | ja | 0.0 | M in C# D in VB | –7,922816E28 = –7,922816 * 10$^{28}$ | +7,922816E28 = 7,922816 * 10$^{28}$ | 29 Stellen | 28 Stellen | nicht def. |
Wie zu sehen ist, unterscheiden sich diese Typen sowohl im Wertebereich als auch in der Genauigkeit. Dies wird unter Hinweise genauer erläutert, ebenso wie die Spalte "unendlich".
    Mit "E38" wird die wissenschaftliche Schreibweise benutzt. Das Single-Maximum bedeutet also eine Zahl mit maximal 39 Stellen, von denen aber nur die ersten 7 Stellen berücksichtigt werden:
Der folgende Code als Teil einer Main-Methode benutzt ein paar dieser Eigenschaften.
* Der kleinstmögliche Single-Wert wird mit sich selbst multipliziert – einmal als Single, einmal als Double.
* Zu einem Betrag wird die Mehrwertsteuer berechnet und addiert.
* In einer Schleife wird ein kleiner Dezimalwert 10.000-Mal addiert: einmal als Double, einmal als Decimal.
Bitte beachten Sie, dass innerhalb von Code immer der Punkt als Dezimaltrenner verwendet werden muss:
Bei direkter Ein- und Ausgabe von Werten hängen Dezimalzeichen und Tausendertrenner auch von der Installation des Rechners und der konkreten Formatierung ab.

## Hinweise

Bei diesen Datentypen handelt es sich um Gleitkommazahlen. Dies ist immer eine näherungsweise Darstellung einer reellen Zahl (in Exponentialdarstellung wie in der Tabelle). Bei Single und Double muss man mit dieser Ungenauigkeit leben; bei Decimal sorgt die interne Verarbeitung der Daten für größere Genauigkeit.

### Unendlich u.a.

Für Single und Double gibt es die folgenden Eigenschaften, was durch das Wort "definiert" in der obigen Spalte "unendlich" deutlich gemacht wird:
* PositiveInfinity steht für "plus unendlich". Es wird angezeigt als Ergebnis, wenn eine positive Zahl durch 0 dividiert wird oder wenn es größer ist als der Maximalwert (wie im obigen ersten Beispiel).
* NegativeInfinity steht entsprechend für "minus unendlich", wenn eine negative Zahl durch 0 dividiert wird oder wenn ein Ergebnis kleiner ist als der Minimalwert.
Dazu gibt es Prüfungen, ob ein Wert unendlich ist; dafür ändern wir das obige Beispiel leicht:
* Beim Wert 0 (der Zahl Null) wird zwischen dem genauen Wert 0, einer positiven 0 und einer negativen 0 unterschieden.
* Als NaN (Not a Number) wird das Ergebnis einer ungültigen Gleitkommaoperation bezeichnet, nämlich die Division von 0 durch 0.
In der Praxis sind diese Situationen eigentlich selten von Bedeutung. Sie müssen aber den folgenden Unterschied bei unzulässigen Berechnungen beachten:
* Bei allen anderen numerischen Datentypen gibt es einen Laufzeitfehler (Exception), wenn der Wertebereich überschritten wird oder eine Division durch 0 erfolgt.
* Bei Single- und Double-Operationen gibt es niemals Laufzeitfehler, sondern stattdessen ein "passendes" Ergebnis wie "unendlich" oder "NaN".
Sie müssen deshalb in allen Fällen selbst eine entsprechende Fehlerbehandlung vorbereiten.

### Genauigkeit

Schon der letzte Teil des einleitenden Beispiels machte deutlich, dass Single und Double ungenau rechnen. Dies ist eine direkte Auswirkung davon, wie Dezimalzahlen von Computern verarbeitet werden. Schauen wir uns zwei einfache Beispiele an:
Die erste Rechnung sollte, wie man im Kopf ausrechnen kann oder direkt sieht, als Ergebnis 0 liefern. Tatsächlich gibt es bei Single und Double unterschiedliche Ergebnisse, die aber immer ungleich 0 sind. Bei Double ist der Wert um 8 Zehnerpotenzen näher an 0, was an der höheren Genauigkeit liegt; aber es bleibt ungleich 0.
Der Vergleich zwischen d3 und d4 zeigt: Wegen der beschränkten Genauigkeit wird die letzte Dezimalstelle bei d4 von vornherein ignoriert. Beide Werte werden also als gleich angesehen.

### Verfahren bei Ungenauigkeiten

Offensichtlich muss man sich Gedanken darüber machen, wie diese Ungenauigkeiten behandelt werden können:
* Das Ergebnis einer Rechnung wird durch Math.Round gerundet; es gibt auch Varianten, die die Genauigkeit dieser Rundung festlegen.
* Das Ausgabeformat bei Console.WriteLine, String.Format, ToString o.ä. erhält eine Vorschrift über die gewünschte Anzahl von Dezimalstellen; das führt automatisch zu einer Rundung.
* Anstelle einer Prüfung auf Gleichheit wird auf näherungsweise Gleichheit geprüft.
Zum letzten Punkt gehören zwei Schritte, die im folgenden Code zusammengefasst werden.

#### Vergleich durch einen Näherungswert

Die beiden Werte, die verglichen werden sollen – im Beispiel das Ergebnis der Rechnung und der konstante Wert 0.0 (der eigentlich überflüssig ist und nur zur Verdeutlichung hingeschrieben ist) – werden voneinander abgezogen; dann wird geprüft, ob der Absolutbetrag der Differenz kleiner ist als die zulässige Abweichung.

#### Festlegung des Näherungswertes

Dieser Wert wird üblicherweise Epsilon genannt; er kann wie im Beispiel vorgegeben werden. Diese Berechnung erfolgt am besten am Anfang des Programms einmalig; der Wert wird "irgendwo" zur wiederholten Verwendung gespeichert.
Hinweis: Single.Epsilon und Double.Epsilon haben nichts damit zu tun; diese sind jeweils der kleinste mögliche Wert, der größer als 0 ist.
Nebenbei gesagt: In der Praxis spielen diese Ungenauigkeiten oft keine Rolle. Man muss sich aber dieser Möglichkeit bewusst sein und darf nicht irgendwann erstaunt aufschreien: ".NET rechnet falsch!"

### Der Datentyp Decimal als Besonderheit

Zur Vermeidung der Ungenauigkeiten wurde der Datentyp Decimal eingeführt, dessen Werte intern anders gespeichert und verarbeitet wird als bei Single oder Double.
Der Decimal-Wertetyp ist für finanzmathematische Berechnungen geeignet, bei denen zahlreiche signifikante Vor- und Nachkommastellen erforderlich sind und keine Rundungsfehler auftreten dürfen.
Wie aus den obigen Beispielen deutlich wird, gibt es die für Single und Double beschriebenen Probleme beim Rechnen und Vergleichen bei Decimal nicht.

### Operationen

Für alle diese Datentypen stehen die folgenden Möglichkeiten zur Verfügung:
* In den Abschnitten zu Datentypen verarbeiten werden behandelt:
** Vergleiche zwischen Werten desselben Typs
** Rechenoperationen zwischen Werten desselben Typs
* In den Abschnitten zu Typumwandlungen werden behandelt:
** Umwandlung einer Zahl in einen String
** Umwandlung eines Strings in eine Zahl
** Umwandlung zwischen Zahlen verschiedener Typen
Mit dem letzten Schritt sind auch Vergleiche und Rechenoperationen zwischen Werte verschiedener Typen möglich.

## Grenzüberschreitung

Die bisherigen Beispiele zeigen schon, was passiert, wenn durch eine Rechnung oder Typumwandlung der maximale Wertebereich überschritten wird:
* Eine Rechnung bei Single oder Double, die den Wertebereich überschreitet, führt zum Ergebnis plus-unendlich bzw. minus-unendlich.
* Ein Single-Wert kann ohne weiteres als Double-Wert verarbeitet werden.
* Eine Rechnung bei Decimal, die den Wertebereich überschreitet, führt wie bei ganzen Zahlen zu einer OverflowException.
Ohne genauere Prüfungen können wir außerdem für Double-Werte feststellen:
* Wenn er den Single-Wertebereich überschreitet, wird er als plus-unendlich bzw. minus-unendlich verstanden.
* Wenn er im Single-Wertebereich bleibt, kann er direkt verarbeitet werden, verliert dabei aber an Genauigkeit.
* Wie bei ganzen Zahlen ist es auch möglich, mit der Modulo-Operation aus dem Double-Wert einen passenden Single-Wert zu errechnen.
Die beiden letzten Situationen werden im folgenden Beispiel gezeigt:
Sie sehen im ersten Fall und beim letzten Schritt, dass nur die Genauigkeit verloren geht. Ob diese Modulo-Funktion in der Praxis sinnvoll ist, lassen wir einmal offen; wahrscheinlich wird man eher eine glatte Zehnerpotenz dafür verwenden.

## Welchen Typ soll ich nehmen?

Wegen der Unterschiede zwischen Single bzw. Double sowie Decimal ist diese Entscheidung ganz einfach:
* Bei wissenschaftlich-technischen Problemen oder Berechnungen des Alltags ist eine Genauigkeit von mehr als sieben Ziffern selten erforderlich; deshalb genügt Single.
* Wenn es auf mehr Ziffern oder größere Werte als 10$^{38}$ ankommt, ist Double zu verwenden. Es spricht auch nichts dagegen, dies als Standard zu nutzen.
* Bei finanzmathematischen Berechnungen darf es keine Ungenauigkeiten geben; dafür muss Decimal gewählt werden.

## Zusammenfassung

Es gibt mehrere Datentypen für Dezimalzahlen, die sich im Wertebereich und in der Genauigkeit unterscheiden.
* Am häufigsten wird System.Double verwendet, der Datentyp für doppelte Genauigkeit.
* Rechenoperationen mit Single und Double bringen häufig Ungenauigkeiten; direkte Vergleiche können in die Irre führen.
* Rechenoperationen mit Decimal weisen eine erheblich größere Genauigkeit ohne Rundungsfehler auf.
Wir können uns also merken:

## Übungen

Hinweis: Wenn hier von Datentypen gesprochen wird, sind immer die drei hier behandelten Typen für Dezimalzahlen gemeint.
| Übung 1 | Definitionen | Zur Lösung |
Welche der folgenden Aussagen sind richtig, welche sind falsch?
* Alle Datentypen haben nur eine begrenzte Genauigkeit.
* Decimal hat eine größere Genauigkeit als Double.
* Die Genauigkeit von Single und Double unterscheidet sich nicht.
* Der Wertebereich von Decimal ist größer als derjenige von Single.
* Für alle Datentypen steht die Größenangabe "minus-unendlich" zur Verfügung.
| Übung 2 | Wertebereich | Zur Lösung |
Welche Datentypen können die folgende Werte bearbeiten? Bitte geben Sie jeweils an, ob die Bearbeitung genau oder mit Rundung erfolgt.
Mehrere Antworten sind möglich. Der Punkt ist als Dezimaltrenner zu verstehen.
* 123.456
* –789.01234
* 1234567890
* 12345678901234567890
* 123456789012345678901234567890
* 1234567890123456789012345678901234567890
| Übung 3 | Wertebereich | Zur Lösung |
* Wie lautet das Ergebnis als Single-Datentyp, wenn Single.MaxValue mit 1.5 multipliziert wird?
* Wie lautet das Ergebnis als Double-Datentyp, wenn Single.MaxValue mit 10 multipliziert wird? Es genügt ein ungefährer Wert.
* Wie lautet das Ergebnis als Decimal-Datentyp, wenn Decimal.MaxValue mit 2 multipliziert wird?
| Übung 4 | Genauigkeit | Zur Lösung |
Welche der folgenden Aufgaben kann ein genaues Ergebnis liefern, welche nicht?
* Single: 123 * 456
* Single: 12.3 * 4.56
* Single: 9876 * 6789
* Double: 9876 * 6789
* Double: 987.6 * 6.789
* Double: 1.23 / 0.41
* Decimal: 1.23 / 0.41
| Übung 5 | Rundungsfehler behandeln | Zur Lösung |
Ein Programm soll zwei verschiedene komplizierte Berechnungen mit Double-Werten ausführen. Wie ist die Prüfung vorzunehmen, ob die beiden Berechnungen dasselbe Ergebnis liefern?
| Lösung zu Übung 1 | Definitionen | Zur Übung |
Die Aussagen 1, 2 sind richtig, die Aussagen 3, 4, 5 sind falsch.
| Lösung zu Übung 2 | Wertebereich | Zur Übung |
* Single (fast genau), Double (fast genau), Decimal (genau); im Rahmen der Darstellung alles genau
* Single (ungenau), Double (fast genau), Decimal (genau)
* Single (ungenau), Double (genau), Decimal (genau)
* Single, Double (jeweils ungenau), Decimal (genau)
* Single, Double (jeweils ungenau)
* Double (ungenau)
| Lösung zu Übung 3 | Wertebereich | Zur Übung |
* Single.PositiveInfinity
* + 3,402823E39 = 3,402823 * 10$^{39}$
* OverflowException
| Lösung zu Übung 4 | Genauigkeit | Zur Übung |
Aufgaben 1, 4, 7 werden immer genau berechnet. Die Lösung von Aufgabe 3 ist wegen der vorgegebenen Genauigkeit des Datentyps immer ungenau. Die Ergebnisse zu den Aufgaben 2, 5, 6 können wegen Rundungsfehlern ungenau sein.
| Lösung zu Übung 5 | Rundungsfehler behandeln | Zur Übung |
Es kann auf jeden Fall zu Rundungsfehlern kommen. Deshalb kann keine exakte Gleichheit, sondern nur eine näherungsweise Gleichheit geprüft werden. Dies geht mit einem Verfahren wie folgt:
* ↑ Quelle: Maschinengenauigkeit