C#: Welche Variable für Währung nehmen

6 Antworten

Vom Fragesteller als hilfreich ausgezeichnet

Also, weil hier etwas Unklarheit herrscht möchte ich das Problem von Float und Double etwas ausführlicher beschreiben. Es ist ein Detail, das selbst manche Programmierer und Informatiker oft nicht beachten (oder nicht wissen?) und m.E.. sehr wichtig für das Verständnis ist. Hier tummeln sich ja auch viele Computerfans/freaks.

Floats und doubles speichern intern die Mantisse Mb (die Nachkommastellen) und einen Exponenten Eb. Beide aber (ausser bei einigen alten Rechnern aus früheren Zeiten) im 2er System (daher habe ich b dahinter geschrieben). Die Nachkommastellen sind also Nachkommastellen einer Binärzahl. Der Exponent ist die "Hochzahl" des Faktors, mit dem die Mantisse zu multiplizieren ist. Die interne Darstellung ist also Mb * 2^Eb (auch wenn sie von der print Funktion als Md*10^Ed - also als Zehnerpotenz ausgegeben wird).

Vereinfacht (weil nicht richtig, aber unten richtig gestellt): könnte z.B. die Zahl 4.25 als
Mb= 1x4 + 0x2 + 0x1 +0x(1/2) + 1x(1/4) + 0x(1/8) + 0.... und Eb=0 dargestellt werden, oder die Zahl 16.0 als Mb= 1x2 + 0x1 + 0x(1/2) + 0x1(4) + 0... und Eb=3.

Nun die Richtigstellung: tatsächlich werden die Zahlen normiert - d.h. zunächst wird der Exponent solange erhöht oder erniedrigt, bis das höchste Bit in der Mantisse die Wertigkeit 1 hat. Da dann die Mantisse immer 1.xxxxxx ist, braucht man die 1 nicht mit zu speichern, und man spart 1 bit (man kann also eine 24bit Mantisse in 23bit darstellen, wenn beim Rechnen intern die fehlende 1 wieder "dazugeschummelt" wird).

E und M werden bei floats in 32bit bzw. 64bit bei double gepackt. Typischerweise 8 bit für Exponent und 23 f. Mantisse bzw. 11 bit Exponent und 52 für Mantisse. (1 weiteres bit braucht man noch für das Vorzeichen).

Nun sollte schon klar werden, dass sich nur Summen von 2er Potenzen exakt darstellen lassen. Zahlen wie 0.1 oder 0.3 führen in dieser Darstellung zu einer periodischen, nicht abbrechenden Binärdarstellung (das was die meisten von uns von 1/3 in Dezimaldarstellung kennen).

Die read Funktion macht also aus 0.1 eine Mantisse 001100110011... Und die print-Funktion rundet auf dem letzten Bit und da kommt dann in diesem Fall wieder 0.1 beim print raus (weil der Fehler im letzten bit kleiner 1/2 e^-X ist).

Wenn man aber z.B. 0.3 * 100.0 - 30.0 ausrechnet, hat sich der Fehler durch die Multiplikation deutlich vergrössert, und man erhält 3.5527136788005E-15.

Wenn man nur genug Zahlen zusammenrechnet, dabei auch noch multipliziert oder dividiert, kann der Fehler dann leicht (insbes. bei grossen Beträgen) in den Cent Bereich rutschen, und unser Protest wäre gross, wenn auf dem Bankauszug immer Centbeträge fehlen würden (tatsächlich gibt es noch andere Fehler, die insbes. bei der Zinsberechnung auftreten - da gibt es dann Rundungen, die nichts mit der Darstelliung von floats zu tun haben)

Ein weiteres Problem ist, dass die Genauigkeit immer relativ zur Grösse der Dargestellten Zahl ist. Bei einem Exponenten von 64 ist ein Fehler im letzten bit schon recht gross (ca. 2^32 also 4 Milliarden), bei einem Exponenten von -64 sehr klein. Aber wenn man diese beiden Zahlen addiert, geht dabei der Wert der kleineren komplett verloren (also sagt der Rechner: 1e60 + 1 == 1e60 -> true, was natürlich gelogen ist. Das klingt jetzt mal nicht so schlimm, aber was ist bei 1e32 + 1? Auch gleich! Und 1E32 sind ja rund 4Milliarden - ein Betrag, der bei der Berechnung des Staatshaushalts schon mal vorkommen kann (wenn auch mit negativem Vorzeichen). Will man Cent-genau rechnen, fängt es (unabhängig von Rundingsfehlern) schon bei rund 40Mio an, sehr ungenau zu werden (bei float). bei doubles hat man etwas (naja) mehr Luft.

Informatiker empfehlen daher (nein sie verlangen es), dass Geldbeträge (und andere Grössen, bei denen es auf ein exaktes Resultat ankommt) nicht in Float, sondern als sogenannte Decimals (auch FixedPoint in manchen Programmiersprachen) berechnet werden. Diese verwenden eine andere interne Darstellung (technisch als Bruch mit einer Zehnerpotenz im Nenner. Sowohl Nenner als auch Zähler als Integer.

Leider haben nicht alle Programmiersprachen solche Datentypen eingebaut (tatsächlich die wenigsten - glücklich, wer z.B in Smalltalk programmiert, ärgerlich für C, C++, Java und viele andere). Daher muss man oft auf Bibliotheksfunktionen zurückgreifen.

sarahj  20.01.2015, 22:26

Danke für's Sternchen.

0

meine variablen sind immer nur einzelne buchstaben. eigentlich nicht so sinnvoll, denn wenn man zu viele variablen hat, dann kenne ich mich immer nicht mehr aus :D

du kannst die variable ja einfach g (für geld) nennen. wenn du mehrere werte ausrechnen willst, dann kannst du auch g1, g2, g3,... nehmen. und auf alle fälle vom typ double :)

mondieu90  11.01.2015, 16:49

Hier ging es nicht um einen Namen für die Varialbe sondern einen Variblentyp. Also int, double, float, short, string....

0
Lisalein632  11.01.2015, 16:49
@mondieu90

letzter satz. da stehts. ich würde es so machen, aber ob das die beste idee ist, das weiß ich leider auch nicht :D

0

Einen Ganzzahldatentypen und dann den Betrag in Cent verwalten.

Es gibt einen 128-bit Gleitkommadatentypen namens "decimal", aber auch das ist letztlich ein Gleitkommadatentyp. Den kannst Du aber vorübergehend zum Einlesen/Ausgeben verwenden, wenn Du nicht Centbeträge einlesen/ausgeben möchtest.

Hallo. Das Problem ist, dass Float und Double immer einen Rundungsfehler haben. Beispiel:

float x=1; x+=1000000000; x-=1000000000;

wenn du dir nun x ausgibst ist das Ergebnis aufgrund von Rundungsfehlern 0. Daher würde ich dir empfehlen einen int zu nehmen und den Betrag in Cent zu speichern. Bei der Ausgabe dann einfach immer den Wert durch 100 Teilen und ausgeben. So kommt es nicht zu derartigen Fehlern.

Edit: Und das rechnen mit ganzen Zahlen ist nebenbei gesagt noch sehr effizient in Bezug auf Speicherplatz und Rechenaufwand.

PWolff  11.01.2015, 17:00

Wenn man aber mit Mehrwertsteuer rechnet und zwei-, dreimal hin- und herrechnet, erhält man bei einzelnen Cent als Basiseinheit zu oft Rundungsfehler im Cent-Bereich. Deshalb hatte Microsoft früher mal einen eigenen Datentyp "Currency", der konstant vier Nachkommastellen mitführte. Seit es diesen Typ nicht mehr gibt, empfiehlt Microsoft den Typ Decimal, der ein Zwischending zwischen Fließkomma- und Integer-Zahl ist.

0