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.