Otages COBOL et Math. partie 1

Avouons-le: personne n'aime les nombres fractionnaires - même les ordinateurs.

En ce qui concerne le langage de programmation COBOL, la première question qui se pose dans la tête de tout le monde ressemble toujours à ceci: "Pourquoi l'humanité utilise-t-elle encore ce langage dans de nombreux domaines vitaux?" Les banques utilisent toujours COBOL. Environ 7% du PIB américain dépend de COBOL pour le traitement des paiements à partir de la CMS . L'Internal Revenue Service (IRS) des États-Unis, comme chacun le sait, utilise toujours COBOL. Cette langue est également utilisée dans l'aviation (à partir de là, j'ai appris une chose intéressante à ce sujet: le numéro de réservation sur les billets d'avion était un pointeur habituel). On peut dire que de nombreuses organisations très sérieuses, qu'il s'agisse d'un secteur privé ou public, utilisent toujours COBOL.



La deuxième partie

L'auteur du document, dont nous publions aujourd'hui la première partie de la traduction, va trouver une réponse à la question de savoir pourquoi COBOL, la langue apparue en 1959, est toujours aussi répandue.

Pourquoi COBOL est-il toujours vivant?


La réponse traditionnelle à cette question est profondément cynique. Les organisations sont la paresse, l'incompétence et la stupidité. Ils recherchent le bon marché et ne sont pas enclins à investir dans la réécriture de leurs systèmes logiciels sur quelque chose de moderne. En général, on peut supposer que la raison pour laquelle le travail d'un nombre aussi important d'organisations dépend de COBOL est une combinaison d'inertie et de myopie. Et en cela, bien sûr, il y a une part de vérité. La réécriture de grandes quantités de code déroutant est une tâche énorme. C'est cher. C'est compliqué. Et si le logiciel existant semble bien fonctionner, l'organisation ne sera pas particulièrement motivée à investir dans un projet de mise à jour de ce logiciel.

Tout cela est vrai. Mais quand j'ai travaillé à l'IRS, les vétérans de COBOL ont expliqué comment ils avaient essayé de réécrire le code en Java et il s'est avéré que Java ne pouvait pas effectuer les calculs correctement.

Cela me semblait extrêmement étrange. C’est tellement étrange que j’ai immédiatement pensé à l’alarmiste: "Seigneur, cela signifie que l’IRS arrondit les impôts à tout le monde depuis 50 ans !!!" Je ne pouvais tout simplement pas croire que COBOL est capable de contourner Java en termes de calculs mathématiques nécessaires à l'IRS. Finalement, ils n’ont pas lancé de personnes dans l’espace.

L'un des effets secondaires intéressants de l'apprentissage du COBOL pendant l'été est que j'ai commencé à comprendre ce qui suit. Le fait n'est pas que Java ne peut pas effectuer correctement des calculs mathématiques. Le point est exactement comment Java rend les calculs corrects. Et lorsque vous comprenez comment les calculs sont effectués en Java et comment la même chose est effectuée dans COBOL, vous commencez à comprendre pourquoi de nombreuses organisations ont du mal à se débarrasser de leur héritage informatique.

Quel "i" doit être pointillé?


Je vais m'éloigner un peu de l'histoire COBOL et parler de la façon dont les ordinateurs stockaient les informations avant que la représentation binaire des données ne devienne la norme de facto (mais le matériel sur la façon d'utiliser l'interface z / OS; c'est quelque chose de spécial). Je pense qu'en examinant notre problème, il sera utile de s'écarter du thème principal dans cette direction. Dans le document ci-dessus, j'ai parlé de différentes façons d'utiliser des commutateurs binaires pour stocker des nombres dans des systèmes binaires, ternaires, décimaux, pour stocker des nombres négatifs - et ainsi de suite. La seule chose à laquelle je n'ai pas prêté suffisamment d'attention était de savoir comment les nombres décimaux sont stockés.

Si vous avez conçu votre propre ordinateur binaire, vous pouvez commencer par décider d'utiliser le système numérique binaire. Les bits à gauche du point représentent des entiers - 1, 2, 4, 8. Et les bits à droite - nombres fractionnaires - 1/2, 1/4, 1/8 ...


2,75 en représentation binaire

Le problème ici est de comprendre comment stocker le point décimal lui-même (en fait - je devrais dire «point binaire» - car, après tout, nous parlons de nombres binaires). Ce n'est pas une sorte d'alchimie informatique, vous pouvez donc bien deviner de quoi je parle des nombres à virgule flottante et des nombres à virgule fixe. Dans les nombres à virgule flottante, un point binaire peut être placé n'importe où (c'est-à-dire qu'il peut «flotter»). La position du point est stockée en tant qu'exposant. La possibilité de déplacer un point permet de stocker une gamme de nombres plus large que celle disponible en l'absence d'une telle opportunité. Le point décimal peut être déplacé tout à l'arrière du nombre et sélectionner tous les bits pour stocker des valeurs entières, représentant de très grands nombres. Le point peut être déplacé vers l'avant du nombre et exprimer de très petites valeurs. Mais cette liberté vient au prix de la précision. Jetons un autre regard sur la représentation binaire de 2,75 de l'exemple précédent. Une transition de quatre à huit est bien plus qu'une transition d'un quart à un huitième. Il peut être plus facile pour nous d'imaginer cela si nous réécrivons l'exemple comme indiqué ci-dessous.


J'ai choisi la distance entre les nombres à l'œil nu - juste pour démontrer mon idée

La différence entre les nombres est facile à calculer par vous-même. Par exemple, la distance entre 1/16 et 1/32 est de 0,03125, mais la distance entre 1/2 et 1/4 est déjà de 0,25.

Pourquoi est-ce important? Dans le cas d'une représentation binaire d'entiers, cela n'a pas d'importance - la distance entre les nombres adjacents d'un enregistrement binaire peut être facilement compensée en les remplissant avec les combinaisons appropriées de bits et sans perte de précision. Mais dans le cas de la représentation des nombres fractionnaires, ce n'est pas si simple. Si vous essayez de "remplir" les "trous" entre des nombres adjacents - quelque chose peut "tomber" (et réellement tomber) dans ces trous. Ceci conduit au fait qu'en format binaire il n'est pas possible d'obtenir des représentations exactes de nombres fractionnaires.

Ceci est illustré par l'exemple classique du nombre 0,1 (un dixième). Comment représenter ce nombre au format binaire? 2 -1 est 1/2 ou 0,5. C'est trop. 1/16 est 0,0635. C'est trop peu. 1/16 + 1/32 est déjà plus proche (0,09375), mais 1/16 + 1/32 + 1/64 est déjà plus que ce dont nous avons besoin (0,109375).

Si vous croyez que ce raisonnement peut être poursuivi indéfiniment - alors vous avez raison - tel qu'il est .

Ici, vous pouvez vous dire: «Pourquoi ne sauvegardons-nous pas simplement 0,1 de la même manière que nous stockons le numéro 1? Nous pouvons enregistrer le numéro 1 sans aucun problème. Supprimons simplement le point décimal et stockons tous les nombres de la même manière que nous stockons les entiers. "

Il s'agit d'une excellente solution à ce problème, sauf qu'elle nécessite de fixer le point binaire / décimal à un emplacement prédéterminé. Sinon, les nombres 10.00001 et 100000.1 seront exactement identiques. Mais si le point est fixé de manière à ce que, disons, 2 chiffres soient attribués à la partie fractionnaire du nombre, alors nous pouvons arrondir 10,00001 à 10,00 et 100000,1 tournera à 100000,10.

Nous venons «d'inventer» des nombres à virgule fixe.

Avec la représentation de différentes valeurs en utilisant des nombres à virgule fixe, nous venons de le comprendre. C'est facile à faire. Est-il possible, en utilisant des nombres à virgule fixe, de faciliter la solution de certains autres problèmes? Rappelons-nous ici de nos bons amis - des nombres décimaux binaires (Binary Coded Decimal, BCD). Soit dit en passant, pour vous faire savoir, ces chiffres sont utilisés dans la plupart des calculatrices scientifiques et graphiques. De ces appareils, ce qui est assez clair, ils attendent les résultats corrects des calculs.


Calculatrice TI-84 Plus

Ratio de récurrence de Muller et Python


Les nombres à virgule fixe sont considérés comme plus précis en raison du fait que les «trous» entre les nombres sont constants, et parce que l'arrondi se produit uniquement lorsque vous avez besoin d'imaginer un nombre pour lequel il n'y a tout simplement pas assez d'espace. Mais lorsque vous utilisez des nombres à virgule flottante, nous pouvons représenter des nombres très grands et très petits en utilisant la même quantité de mémoire. Certes, avec leur aide, il est impossible de représenter avec précision tous les nombres dans la plage accessible et nous sommes obligés de recourir à l'arrondissement afin de combler les «trous».

COBOL a été créé comme une langue dans laquelle, par défaut, des nombres à virgule fixe sont utilisés. Mais cela signifie-t-il que COBOL est meilleur que les langages modernes pour effectuer des calculs mathématiques? Si nous nous attaquons à un problème comme le résultat du calcul de la valeur 0,1 + 0,2, alors il peut sembler que la question précédente doit être répondue «oui». Mais ce sera ennuyeux. Continuons donc.

Nous allons expérimenter COBOL en utilisant la relation dite de récurrence de Muller. Jean-Michel Muller est un scientifique français qui a peut-être fait une découverte scientifique majeure dans le domaine des technologies de l'information. Il a trouvé un moyen de briser le bon fonctionnement des ordinateurs en utilisant les mathématiques. Je suis sûr qu'il dirait qu'il étudie les problèmes de fiabilité et de précision, mais non et encore: il crée des problèmes mathématiques qui «cassent» les ordinateurs. L'une de ces tâches est sa formule de récurrence. Cela ressemble à ceci:


Cet exemple est tiré d'ici.

La formule ne semble pas du tout effrayante. Non? Cette tâche convient à nos besoins pour les raisons suivantes:

  • Seules des règles mathématiques simples sont utilisées ici - pas de formules compliquées ou d'idées profondes.
  • Nous commençons par un nombre qui a deux chiffres après la virgule décimale. Par conséquent, il est facile d'imaginer que nous travaillons avec des valeurs représentant certaines sommes d'argent.
  • L'erreur qui résulte des calculs n'est pas une petite erreur d'arrondi. Il s'agit d'une déviation du résultat correct par des ordres de grandeur entiers.

Voici un petit script Python qui calcule les résultats de la relation de récurrence de Mueller en utilisant des nombres à virgule flottante et à virgule fixe:

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]) 

Voici le résultat de ce script:

 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 

Jusqu'à l'itération 12, l'erreur d'arrondi semble plus ou moins insignifiante, mais le véritable enfer commence. Les calculs à virgule flottante convergent vers un nombre qui est vingt fois plus élevé que celui résultant des calculs à virgule fixe.

Vous pensez peut-être qu'il est peu probable que quiconque effectue des calculs récursifs à si grande échelle. Mais c'est précisément ce qui a causé la catastrophe de 1991, qui a entraîné la mort de 28 personnes, lorsque le système de contrôle des missiles Patriot a mal calculé l'heure. Il s'est avéré que les calculs en virgule flottante ont accidentellement fait beaucoup de mal. Voici quelques trucs formidables que l'informatique hautes performances n'est peut-être qu'un moyen plus rapide d'obtenir les mauvaises réponses. Lisez ce travail si vous souhaitez obtenir plus d'informations sur le problème discuté ici et voir plus d'exemples.

Le problème est que la quantité de RAM que les ordinateurs ont n'est pas infinie. Par conséquent, il est impossible de stocker un nombre infini de positions décimales (ou binaires). Les calculs à virgule fixe peuvent être plus précis que les calculs à virgule flottante s'il est certain qu'il est moins probable que plus de nombres seront nécessaires après le point que le format utilisé. Si le nombre ne rentre pas dans ce format, il sera arrondi. Il convient de noter que ni les calculs à virgule fixe ni les calculs à virgule flottante ne sont protégés du problème que la relation de récurrence de Mueller démontre. Cela et d'autres en conséquence donnent des résultats incorrects. La question est de savoir quand cela se produit. Si vous augmentez le nombre d'itérations dans un script Python, par exemple, de 20 à 22, le nombre final obtenu dans les calculs avec un point fixe sera 0,728107. 23 itérations? -501.7081261. 24? 105.8598187.

Dans différentes langues, ce problème se manifeste de différentes manières. Certains, comme COBOL, vous permettent de travailler avec des nombres dont les paramètres sont bien définis. Et en Python, par exemple, il existe des valeurs par défaut qui peuvent être configurées si l'ordinateur a suffisamment de mémoire. Si nous ajoutons la ligne getcontext().prec = 60 à notre programme, indiquant au module décimal Python qu'il utiliserait 60 positions après la période, plutôt que 28, comme cela est fait par défaut, le programme sera en mesure de terminer 40 itérations de la relation de récurrence sans erreurs Mueller.

À suivre ...

Chers lecteurs! Avez-vous rencontré de sérieux problèmes dus à la nature des calculs à virgule flottante?

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


All Articles