Wenn Computermathe nicht aufgeht

Dec 07 2022
Hinzufügen von Fließkommazahlen Mathematik ist schwierig, und wenn Sie die physikalischen Einschränkungen des Computerspeichers einführen, ist Mathematik schwieriger. Wir müssen oft grundlegende Mathematik in Software machen.

Fließkommazahlen addieren

Mathe ist schwer, und wenn Sie die physikalischen Einschränkungen des Computerspeichers einführen, ist Mathe schwieriger.

Wir müssen oft grundlegende Mathematik in Software machen. Ein häufig auftretendes praktisches Problem ist das Zusammenzählen von Geldbeträgen.

Sally hat zwei Artikel in ihrem Einkaufswagen. Der erste Artikel kostet 12,33 $. Der zweite Artikel kostet 15,68 $. Wir sollten berechnen können, dass Sallys Gesamtbetrag 12,33 $ + 15,68 $ = 28,01 $ beträgt. Auf dem Papier ist dies ein leicht zu lösendes Problem.

Wenn ich jedoch eine interaktive Ruby-Sitzung eröffne und diese beiden Fließkommazahlen addiere, erhalte ich einen scheinbar verrückten Rundungsfehler.

Computer sollen Berechnungen erleichtern, und doch wird etwas so Einfaches wie Addition sehr unintuitiv. Softwareentwickler wissen, dass nicht intuitives Verhalten im Code Fehler verursacht. Was ist also die Ursache für diesen Rundungsfehler?

Wert darstellen

Wir kommen zur Mathematik, aber lassen Sie uns zuerst über die Darstellung von Werten sprechen. Wir haben mehrere Möglichkeiten, Werte darzustellen. Zum Beispiel, um mein Alter darzustellen:

In geschriebenem Englisch: neunundzwanzig

Mit römischen Ziffern: XXIX

In einer Höhle:

Basis 10: 29

Der größte Teil unserer Welt verwendet ein System zur Basis 10, um Zahlen darzustellen, obwohl es einige interessante Ausnahmen gibt . Die Verwendung der Basis 10 stammt wahrscheinlich von Menschen, die 10 Finger zum Zählen haben. In einem System zur Basis 10 werden Werte mit 10 verschiedenen Zahlen dargestellt: 0,1,2,3,4,5,6,7,8,9.

Sie erinnern sich vielleicht, dass Ihr Mathematiklehrer eine Zahl an der Einerstelle, Zehnerstelle, Hunderterstelle usw. genannt hat. Beachten Sie, dass dies alles Potenzen von 10 sind: 10⁰, 10¹, 10².

Dies mag wie eine Überkomplikation erscheinen, aber es wird uns helfen, das Offensichtliche über das Zählen zur Basis 10 auf andere Systeme abzubilden – wie binär!

Binär ist nur ein weiteres System zur Darstellung von Werten. Anstelle von Basis 10 verwenden wir Basis 2. Binär ist für diese Diskussion wichtig, weil Computer Daten im Speicher als Binär speichern . Im Binärsystem werden Zahlen mit nur zwei Werten dargestellt: 0 und 1.

Wie stelle ich also mein Alter binär dar? Es wird als 11101 dargestellt. Diese Darstellung ist einfach abzuleiten. Anstelle von Werten an der Einerstelle, der Zehnerstelle und der Hunderterstelle hat Binär Werte an der Einerstelle, der Viererstelle, der Achterstelle usw.: 2⁰, 2¹, 2², 2³, 2⁴.

Darstellung von Bruchwerten

Wie bei ganzen Zahlen können auch Bruchwerte sowohl zur Basis 10 als auch binär dargestellt werden.

Lassen Sie uns zur Übung ⅛ in beiden Systemen als Dezimalzahl darstellen. Bei Dezimalzahlen zur Basis 10 haben wir Werte in Zehntel-, Hundertstel- und Tausendstelstellen usw.

Und wenn wir uns mit Dezimalzahlen im Binärformat befassen, haben wir die Hälften, Viertel- und Achtelstellen usw.

Periodische Dezimalzahlen

Ich kann endlich jede ganze Zahl darstellen, die ich sowohl in der Basis 10 als auch in binär haben möchte. Es ist jedoch nicht möglich, alle Bruchwerte endlich darzustellen. Denken Sie zum Beispiel daran, dass sich in der Basis 10 die Dezimaldarstellung von ⅓ unendlich wiederholt .

Warum können wir also ⅓ nicht endlich zur Basis 10 darstellen?

Mathematisch gesehen können wir ⅓ nicht endlich in der Basis 10 darstellen, weil 3 kein Faktor von 10 ist. Ein weiteres mathematisches Konzept der Grundschule, an das Sie sich vielleicht erinnern, ist die Primfaktorzerlegung – das Zerlegen einer Zahl in eine Reihe von Primzahlen, die, wenn sie wieder miteinander multipliziert werden , ergeben Ihre ursprüngliche Nummer. Die Primfaktorzerlegung von 10 = 2⋅5. Das heißt, wenn der Nenner eines gekürzten Bruchs in 2er und 5er zerlegt werden kann, dann kann der Wert endlich zur Basis 10 dargestellt werden.

Für binär ist die Primfaktorzerlegung von 2 = 2⋅1. Damit eine Dezimalzahl binär endlich darstellbar ist, muss der Nenner ein Vielfaches von 2 sein.

Um es noch einmal zu wiederholen: Wenn der Nenner eines reduzierten Bruchs in 2er und 5er zerlegt werden kann, ist er endlich in der Basis 10 darstellbar. Wenn der Nenner eines reduzierten Bruchs ein Vielfaches von 2 ist, ist er endlich binär darstellbar. Andernfalls sind die Dezimalstellen periodisch und wiederholen sich unendlich . Beachten Sie in der folgenden Tabelle, dass die Brüche ⅕, ¹⁄₁₀₀, ³³⁄₁₀₀ und ⁶⁸⁄₁₀₀ endlich zur Basis 10 darstellbar sind, aber NICHT binär.

Die Tatsache, dass Dezimalzahlen zur Basis 10 binär nicht immer endlich darstellbar sind, ist der Grund dafür, dass wir beim Addieren von Gleitkommazahlen unerwartete Rundungsfehler bekommen.

Schwebt im Speicher

Computer haben nicht unendlich viel Platz zum Speichern von Gleitkommawerten. Die Floats in meiner interaktiven Ruby-Session erhalten 64 Bit Speicherplatz.

Eine Zahl in 64-Bit-Double-Precision-IEEE-754-Binär-Gleitkomma-Standarddarstellung erfordert drei Bauelemente: Zeichen (es dauert 1 Bit und es ist entweder 0 für positive oder 1 für negative Zahlen), Exponent (11 Bit), Mantisse (52 Bit).

Kommen wir noch einmal auf unser ursprüngliches Problem zurück, Geldsummen zusammenzurechnen: 12,33 $ + 15,68 $ = 28,01 $

In meinem Computer wird 12.33 endlich und ungenau gespeichert als:

0–10000000010–1000101010001111010111000010100011110101110000101001

0–10000000010–1111010111000010100011110101110000101000111101011100

12.330000000000000071054273576 + 15.679999999999999715782905696 = 28.009999999999999786837179272

Als Referenz habe ich diese Website verwendet , um die Konvertierungen zwischen Dezimal- und 64-Bit-Form durchzuführen.

Lösung des Problems

  • Führen Sie Integer-Mathematik anstelle von Gleitkomma-Mathematik durch (und wandeln Sie sie zurück in die Dezimalform, wenn Sie fertig sind)
  • Verwenden Sie Bibliotheken, die speziell für den Umgang mit Bargeld entwickelt wurden
  • Ruby hat eine native Big Decimal-Klasse, die „eine ähnliche Unterstützung für sehr große oder sehr genaue Gleitkommazahlen bietet“. Andere Sprachen haben ähnliche Lösungen.

Warum erhalten wir keinen Rundungsfehler, wenn wir diese beiden Gleitkommawerte addieren?