Geiseln COBOL und Math. Teil 1

Seien wir ehrlich: Niemand mag Bruchzahlen - selbst Computer.

Wenn es um die Programmiersprache COBOL geht, sieht die erste Frage, die in jedem Kopf auftaucht, immer so aus: „Warum verwendet die Menschheit diese Sprache immer noch in vielen wichtigen Bereichen?“ Banken verwenden immer noch COBOL. Etwa 7% des US-BIP sind bei der Verarbeitung von Zahlungen von CMS auf COBOL angewiesen. Wie jeder weiß, verwendet der Internal Revenue Service (IRS) der Vereinigten Staaten immer noch COBOL. Diese Sprache wird auch in der Luftfahrt verwendet ( von hier aus habe ich eine interessante Sache zu diesem Thema gelernt: Die Reservierungsnummer auf Flugtickets war früher ein üblicher Hinweis). Es kann gesagt werden, dass viele sehr ernsthafte Organisationen, ob es sich um einen privaten oder einen öffentlichen Sektor handelt, immer noch COBOL verwenden.



Der zweite Teil

Der Autor des Materials, dessen erster Teil der Übersetzung wir heute veröffentlichen, wird eine Antwort auf die Frage finden, warum COBOL, die Sprache, die 1959 erschien, immer noch so verbreitet ist.

Warum lebt COBOL noch?


Die traditionelle Antwort auf diese Frage ist zutiefst zynisch. Organisationen sind Faulheit, Inkompetenz und Dummheit. Sie sind auf der Suche nach Billigkeit und neigen nicht dazu, in das Umschreiben ihrer Softwaresysteme auf etwas Modernes zu investieren. Im Allgemeinen kann davon ausgegangen werden, dass der Grund dafür, dass die Arbeit einer so großen Anzahl von Organisationen von COBOL abhängt, eine Kombination aus Trägheit und Kurzsichtigkeit ist. Und darin liegt natürlich etwas Wahres. Das Umschreiben großer Mengen verwirrenden Codes ist eine gewaltige Aufgabe. Ist es teuer. Das ist schwer. Und wenn die vorhandene Software gut zu funktionieren scheint, hat die Organisation keine besonders starke Motivation, in ein Projekt zur Aktualisierung dieser Software zu investieren.

Das alles ist so. Aber als ich beim IRS arbeitete, sprachen COBOL-Veteranen darüber, wie sie versuchten, Code in Java neu zu schreiben, und es stellte sich heraus, dass Java die Berechnungen nicht korrekt ausführen konnte.

Es klang für mich sehr seltsam. Es ist so seltsam, dass ich sofort den alarmierenden Gedanken bekam: "Herr, das heißt, die IRS rundet seit 50 Jahren die Steuerzahlungen an alle ab !!!" Ich konnte einfach nicht glauben, dass COBOL Java in Bezug auf die vom IRS benötigten mathematischen Berechnungen umgehen kann. Am Ende haben sie keine Menschen in den Weltraum gebracht.

Eine der interessanten Nebenwirkungen des Lernens von COBOL im Sommer ist, dass ich Folgendes zu verstehen begann. Der Punkt ist nicht, dass Java mathematische Berechnungen nicht korrekt ausführen kann. Der Punkt ist genau, wie Java die Berechnungen korrekt macht. Und wenn Sie verstehen, wie Berechnungen in Java ausgeführt werden und wie dasselbe in COBOL ausgeführt wird, beginnen Sie zu verstehen, warum es vielen Unternehmen so schwer fällt, ihr Computer-Erbe loszuwerden.

Welches "i" sollte gepunktet werden?


Ich werde mich ein wenig von der COBOL-Geschichte entfernen und darüber sprechen, wie Computer Informationen gespeichert haben, bevor die binäre Darstellung von Daten zum De-facto-Standard wurde (aber das Material zur Verwendung der z / OS- Schnittstelle ; dies ist etwas Besonderes). Ich denke, dass es bei der Betrachtung unseres Themas nützlich sein wird, in dieser Richtung vom Hauptthema abzuweichen. Im obigen Material habe ich über verschiedene Möglichkeiten gesprochen, binäre Schalter zu verwenden, um Zahlen in binären, ternären, dezimalen Systemen zu speichern, negative Zahlen zu speichern - und so weiter. Das einzige, worauf ich nicht genug geachtet habe, war, wie Dezimalzahlen gespeichert werden.

Wenn Sie Ihren eigenen Binärcomputer entworfen haben, können Sie zunächst entscheiden, dass Sie das Binärzahlensystem verwenden. Die Bits links vom Punkt stehen für ganze Zahlen - 1, 2, 4, 8. Und die Bits rechts - Bruchzahlen - 1/2, 1/4, 1/8 ...


2,75 in binärer Darstellung

Das Problem hierbei ist, zu verstehen, wie der Dezimalpunkt selbst gespeichert wird (tatsächlich - ich sollte „Binärpunkt“ sagen -, weil es sich schließlich um Binärzahlen handelt). Dies ist keine Art von „Computeralchemie“, Sie können also gut erraten, wovon ich spreche, von Gleitkommazahlen und Festkommazahlen. In Gleitkommazahlen kann ein Binärpunkt an einer beliebigen Stelle platziert werden (dh er kann „schweben“). Die Position des Punktes wird als Exponent gespeichert. Die Fähigkeit, einen Punkt zu verschieben, ermöglicht es, einen größeren Bereich von Zahlen zu speichern, als ohne eine solche Möglichkeit verfügbar ist. Der Dezimalpunkt kann ganz nach hinten verschoben werden und alle Bits auswählen, um ganzzahlige Werte zu speichern, die sehr große Zahlen darstellen. Der Punkt kann nach vorne verschoben werden und sehr kleine Werte ausdrücken. Diese Freiheit geht jedoch zu Lasten der Genauigkeit. Schauen wir uns noch einmal die binäre Darstellung von 2.75 aus dem vorherigen Beispiel an. Ein Übergang von vier auf acht ist viel mehr als ein Übergang von einem Viertel auf ein Achtel. Es kann für uns einfacher sein, sich das vorzustellen, wenn wir das Beispiel wie unten gezeigt umschreiben.


Ich habe den Abstand zwischen den Zahlen mit dem Auge gewählt - nur um meine Idee zu demonstrieren

Der Unterschied zwischen den Zahlen lässt sich leicht selbst berechnen. Zum Beispiel beträgt der Abstand zwischen 1/16 und 1/32 0,03125, aber der Abstand zwischen 1/2 und 1/4 beträgt bereits 0,25.

Warum ist das wichtig? Bei einer binären Darstellung von ganzen Zahlen spielt dies keine Rolle - der Abstand zwischen benachbarten Zahlen eines binären Datensatzes kann leicht kompensiert werden, indem sie mit den entsprechenden Kombinationen von Bits gefüllt werden, ohne an Genauigkeit zu verlieren. Bei der Darstellung von Bruchzahlen ist dies jedoch nicht so einfach. Wenn Sie versuchen, die "Löcher" zwischen benachbarten Zahlen zu "füllen", kann etwas in diese Löcher "fallen" (und tatsächlich durchfallen). Dies führt dazu, dass es im Binärformat nicht möglich ist, genaue Darstellungen von Bruchzahlen zu erhalten.

Dies zeigt das klassische Beispiel der Zahl 0.1 (ein Zehntel). Wie kann man diese Zahl im Binärformat darstellen? 2 -1 ist 1/2 oder 0,5. Das ist zu viel. 1/16 ist 0,0635. Das ist zu wenig. 1/16 + 1/32 ist bereits näher (0,09375), aber 1/16 + 1/32 + 1/64 ist bereits mehr als wir brauchen (0,109375).

Wenn Sie glauben, dass diese Argumentation auf unbestimmte Zeit fortgesetzt werden kann - dann haben Sie Recht - so wie es ist .

Hier können Sie sich sagen: „Warum speichern wir nicht einfach 0,1 auf die gleiche Weise, wie wir die Nummer 1 speichern? Wir können die Zahl 1 problemlos speichern. Entfernen Sie also einfach den Dezimalpunkt und speichern Sie alle Zahlen auf die gleiche Weise wie Ganzzahlen. "

Dies ist eine hervorragende Lösung für dieses Problem, mit der Ausnahme, dass der Binär- / Dezimalpunkt an einer bestimmten Stelle festgelegt werden muss. Andernfalls sehen die Nummern 10.00001 und 100000.1 genauso aus. Wenn der Punkt jedoch so festgelegt ist, dass dem Bruchteil der Zahl beispielsweise 2 Ziffern zugewiesen werden, können wir 10.00001 auf 10.00 runden, und 100000.1 wird 100000.10.

Wir haben nur Festkommazahlen „erfunden“.

Mit der Darstellung verschiedener Werte unter Verwendung von Festkommazahlen haben wir es gerade herausgefunden. Es ist einfach zu tun. Ist es möglich, mithilfe von Festkommazahlen die Lösung einiger anderer Probleme zu erleichtern? Erinnern wir uns hier an unsere guten Freunde - an binäre Dezimalzahlen (Binary Coded Decimal, BCD). Übrigens, um Sie wissen zu lassen, werden diese Zahlen in den meisten wissenschaftlichen und grafischen Taschenrechnern verwendet. Von diesen Geräten erwarten sie, was ziemlich klar ist, die korrekten Ergebnisse der Berechnungen.


TI-84 Plus Rechner

Wiederholungsrate von Müller und Python


Festkommazahlen werden als genauer angesehen, da die „Löcher“ zwischen den Zahlen konstant sind und nur dann gerundet wird, wenn Sie sich eine Zahl vorstellen müssen, für die einfach nicht genügend Platz vorhanden ist. Bei Verwendung von Gleitkommazahlen können wir jedoch sehr große und sehr kleine Zahlen mit derselben Speichermenge darstellen. Es stimmt, mit ihrer Hilfe ist es unmöglich, alle Zahlen im zugänglichen Bereich genau darzustellen, und wir sind gezwungen, auf Rundungen zurückzugreifen, um die „Löcher“ zu füllen.

COBOL wurde als Sprache erstellt, in der standardmäßig Festkommazahlen verwendet werden. Aber bedeutet dies, dass COBOL für mathematische Berechnungen besser ist als moderne Sprachen? Wenn wir ein Problem wie das Ergebnis der Berechnung des Werts 0,1 + 0,2 feststellen, sollte die vorherige Frage möglicherweise mit „Ja“ beantwortet werden. Aber es wird langweilig. Also lasst uns weitermachen.

Wir werden mit COBOL unter Verwendung der sogenannten Muller's Recurrence-Beziehung experimentieren. Jean-Michel Muller ist ein französischer Wissenschaftler, der möglicherweise eine wichtige wissenschaftliche Entdeckung auf dem Gebiet der Informationstechnologie gemacht hat. Er fand einen Weg, den korrekten Betrieb von Computern mithilfe von Mathematik zu unterbrechen. Ich bin sicher, dass er sagen würde, dass er die Probleme der Zuverlässigkeit und Genauigkeit untersucht, aber nein und nein noch einmal: Er schafft mathematische Probleme, die Computer „kaputt machen“. Eine dieser Aufgaben ist die Wiederholungsformel. Es sieht so aus:


Dieses Beispiel stammt von hier.

Die Formel scheint überhaupt nicht beängstigend. Richtig? Diese Aufgabe ist aus folgenden Gründen für unsere Zwecke geeignet:

  • Hier werden nur einfache Regeln der Mathematik verwendet - keine komplizierten Formeln oder tiefen Ideen.
  • Wir beginnen mit einer Zahl, die zwei Nachkommastellen hat. Daher ist es leicht vorstellbar, dass wir mit Werten arbeiten, die bestimmte Geldbeträge repräsentieren.
  • Der Fehler, der sich aus den Berechnungen ergibt, ist kein kleiner Rundungsfehler. Dies ist eine Abweichung vom korrekten Ergebnis um ganze Größenordnungen.

Hier ist ein kleines Python-Skript, das die Ergebnisse von Muellers Wiederholungsrelation unter Verwendung von Gleitkomma- und Festkommazahlen berechnet:

from decimal import Decimal def rec(y, z):  return 108 - ((815-1500/z)/y)  def floatpt(N):  x = [4, 4.25]  for i in range(2, N+1):   x.append(rec(x[i-1], x[i-2]))  return x  def fixedpt(N):  x = [Decimal(4), Decimal(17)/Decimal(4)]  for i in range(2, N+1):   x.append(rec(x[i-1], x[i-2]))  return x N = 20 flt = floatpt(N) fxd = fixedpt(N) for i in range(N):  print str(i) + ' | '+str(flt[i])+' | '+str(fxd[i]) 

Hier ist das Ergebnis dieses Skripts:

 i | floating pt  | fixed pt -- | -------------- | --------------------------- 0 | 4       | 4 1 | 4.25      | 4.25 2 | 4.47058823529 | 4.4705882352941176470588235 3 | 4.64473684211 | 4.6447368421052631578947362 4 | 4.77053824363 | 4.7705382436260623229461618 5 | 4.85570071257 | 4.8557007125890736342039857 6 | 4.91084749866 | 4.9108474990827932004342938 7 | 4.94553739553 | 4.9455374041239167246519529 8 | 4.96696240804 | 4.9669625817627005962571288 9 | 4.98004220429 | 4.9800457013556311118526582 10 | 4.9879092328  | 4.9879794484783912679439415 11 | 4.99136264131 | 4.9927702880620482067468253 12 | 4.96745509555 | 4.9956558915062356478184985 13 | 4.42969049831 | 4.9973912683733697540253088 14 | -7.81723657846 | 4.9984339437852482376781601 15 | 168.939167671 | 4.9990600687785413938424188 16 | 102.039963152 | 4.9994358732880376990501184 17 | 100.099947516 | 4.9996602467866575821700634 18 | 100.004992041 | 4.9997713526716167817979714 19 | 100.000249579 | 4.9993671517118171375788238 

Bis zur Iteration 12 sieht der Rundungsfehler mehr oder weniger unbedeutend aus, aber dann beginnt die wahre Hölle. Gleitkommaberechnungen konvergieren zu einer Zahl, die zwanzigmal größer ist als die Ergebnisse von Festkommaberechnungen.

Vielleicht halten Sie es für unwahrscheinlich, dass jemand so umfangreiche rekursive Berechnungen durchführen würde. Aber genau dies verursachte die Katastrophe von 1991, die zum Tod von 28 Menschen führte, als das Patriot-Raketenkontrollsystem die Zeit falsch berechnete. Es stellte sich heraus, dass Gleitkommaberechnungen versehentlich großen Schaden anrichteten. Hier sind einige großartige Dinge , mit denen High Performance Computing möglicherweise nur schneller die falschen Antworten liefert. Lesen Sie diese Arbeit, wenn Sie weitere Informationen zu dem hier diskutierten Problem erhalten und weitere Beispiele sehen möchten.

Das Problem ist, dass die RAM-Größe der Computer nicht unendlich ist. Daher ist es unmöglich, eine unendliche Anzahl von Dezimalstellen (oder Binärpositionen) zu speichern. Festkommaberechnungen können genauer sein als Gleitkommaberechnungen, wenn die Gewissheit besteht, dass es weniger wahrscheinlich ist, dass nach dem Punkt mehr Zahlen benötigt werden als das verwendete Format. Wenn die Zahl nicht in dieses Format passt, wird sie gerundet. Es ist zu beachten, dass weder Festpunktberechnungen noch Gleitkommaberechnungen vor dem Problem geschützt sind, das Muellers Wiederholungsrelation zeigt. Sowohl das als auch andere führen zu falschen Ergebnissen. Die Frage ist, wann dies passiert. Wenn Sie die Anzahl der Iterationen in einem Python-Skript beispielsweise von 20 auf 22 erhöhen, beträgt die endgültige Zahl, die bei Berechnungen mit einem festen Punkt erhalten wird, 0,728107. 23 Iterationen? -501.7081261. 24? 105.8598187.

In verschiedenen Sprachen manifestiert sich dieses Problem auf unterschiedliche Weise. Einige, wie COBOL, ermöglichen es Ihnen, mit Zahlen zu arbeiten, deren Parameter eng eingestellt sind. In Python gibt es beispielsweise Standardwerte, die konfiguriert werden können, wenn der Computer über genügend Speicher verfügt. Wenn wir unserem Programm die Zeile getcontext().prec = 60 hinzufügen und dem Python-Dezimalmodul mitteilen, dass es nach dem Punkt 60 Positionen und nicht 28 verwenden würde, wie dies standardmäßig der Fall ist, kann das Programm 40 Iterationen der Wiederholungsrelation ohne Fehler ausführen Müller.

Fortsetzung folgt…

Liebe Leser! Sind Sie auf ernsthafte Probleme gestoßen, die sich aus der Art der Gleitkommaberechnungen ergeben?

Source: https://habr.com/ru/post/de467251/


All Articles