Migrer COBOL COMP-3 sans perdre un centime
Une migration COBOL COMP-3 échoue si échelle, signes, arrondis ou octets invalides changent. Modélisez le contrat et vérifiez chaque montant.

La réécriture d'un système monétaire n'a qu'un seuil d'acceptation: une même entrée valide doit produire le même montant, le même signe, le même statut et la même représentation stockée lorsque celle-ci fait encore partie d'une interface. Un centime d'écart n'est pas un défaut cosmétique. Sur un million d'enregistrements, il peut modifier le total d'un grand livre, une file d'exceptions, un palier d'intérêt ou le fichier accepté par un programme en aval.
L'hypothèse dangereuse consiste à croire qu'un champ COBOL correspond à un type moderne. C'est faux. Son sens vient de PICTURE, USAGE, des options du compilateur, des instructions arithmétiques, des champs récepteurs, de la structure des fichiers et parfois de données incorrectes tolérées pendant des décennies. PIC S9(7)V99 COMP-3 décrit un coefficient entier signé, une échelle implicite de deux et un contrat de stockage compacté. Le traiter comme un nombre générique supprime au moins la moitié de ces informations.
J'ai vu des équipes débattre plus longtemps de decimal contre double qu'examiner le MOVE qui supprimait réellement les décimales. Ce débat commence trop tard. Reconstituez d'abord le contrat numérique. Choisissez ensuite une représentation cible capable de l'imposer, puis exécutez les deux systèmes sur le même trafic jusqu'à ce que chaque différence soit expliquée.
La clause PICTURE fait partie de la valeur
La clause PICTURE indique le nombre de chiffres, l'échelle, la présence possible d'un signe et parfois des positions d'échelle qui n'occupent aucun stockage. Aucun de ces éléments ne relève seulement de l'affichage.
Prenons ces déclarations:
01 INVOICE-AMOUNT PIC S9(7)V99 COMP-3.
01 TAX-RATE PIC S9(3)V9(4) COMP-3.
01 WHOLE-DOLLARS PIC S9(9) COMP-3.
01 SMALL-RATIO PIC SV9(6) COMP-3.
V est un séparateur décimal implicite. Aucun octet ne lui correspond en mémoire ou sur disque. INVOICE-AMOUNT stocke neuf chiffres et un signe, et son coefficient 123456789 signifie 1234567.89. TAX-RATE a une échelle de quatre. WHOLE-DOLLARS a une échelle nulle. SMALL-RATIO n'a pas de position entière, donc le coefficient 123456 signifie 0.123456.
Un inventaire de migration doit relever au minimum (signed, precision, scale, usage, byte length) pour chaque élément numérique. Conservez aussi la déclaration d'origine et la structure de l'enregistrement qui le contient. Les copybooks utilisent REDEFINES, OCCURS, des noms de condition et des déplacements de groupes. Un champ ne s'interprète donc pas toujours indépendamment des octets voisins.
Le symbole P demande un traitement distinct. Il décrit des positions d'échelle implicites qui ne sont pas stockées. La documentation d'IBM Enterprise COBOL cite PPP999, dont les chiffres stockés représentent des valeurs de zéro à .000999, et S999PPP, dont les valeurs non nulles progressent par milliers. Un outil qui ne compte que les chiffres stockés manque l'échelle arithmétique. Ne déduisez pas un DECIMAL(p,s) de la longueur en octets.
Produisez un catalogue lisible par machine plutôt qu'une feuille de calcul qui finira par diverger du code. Une entrée utile ressemble à ceci:
{
"qualifiedName": "CLAIM-REC.PAID-AMOUNT",
"picture": "S9(7)V99",
"usage": "COMP-3",
"bytes": 5,
"precision": 9,
"scale": 2,
"signed": true,
"storage": "packed-decimal"
}
Cet artefact constitue le début du contrat de réécriture. Il détecte aussi une erreur classique d'analyse: neuf chiffres en décimal compacté occupent cinq octets, car le dernier demi-octet porte le signe.
Résolvez les alias avant d'attribuer le sens. Une branche REDEFINES peut lire les mêmes cinq octets comme un montant pour un type de transaction, et comme du remplissage ou une date pour un autre. Le discriminant qui choisit la branche appartient au contrat numérique. Si la nouvelle couche d'entrée décode immédiatement toutes les branches possibles, elle peut rejeter des enregistrements valides, car des octets numériques dans une structure sont du texte dans une autre. Notez la condition de contrôle, pas seulement les offsets qui se chevauchent.
Les opérations de groupe exigent l'attention inverse. MOVE OLD-GROUP TO NEW-GROUP copie des octets sans appliquer les conversions numériques des éléments. Le remplacer par une copie champ par champ peut normaliser les signes, changer le remplissage ou décoder un champ que la source ne consultait jamais. Classez chaque usage comme opération binaire ou numérique avant de déclarer qu'un objet typé est équivalent.
Les octets COMP-3 exigent un décodeur
Le décimal compacté place deux chiffres décimaux par octet, sauf dans le demi-octet bas du dernier octet, réservé au signe. L'environnement cible doit valider et décoder explicitement ces demi-octets à chaque frontière externe.
Pour PIC S9(5)V99 COMP-3, la valeur -12345.67, de coefficient -1234567, peut apparaître ainsi:
12 34 56 7D
Lisez les demi-octets comme 1 2 3 4 5 6 7 D. Les sept premiers sont des chiffres. Le D final indique un nombre négatif. Une valeur positive conventionnelle finit par C; une donnée compactée non signée finit souvent par F. Des systèmes réels peuvent contenir d'autres codes selon les options du compilateur et le chemin de production. Le décodeur a donc besoin d'une politique déclarée, pas d'une conversion hexadécimale permissive.
La longueur en octets de n chiffres stockés vaut floor(n / 2) + 1. Si le nombre de chiffres est pair, le premier demi-octet est du remplissage. Il doit tout de même être validé. IBM indique que NUMCHECK(PAC) contrôle chiffres et signes compactés quand les champs servent d'émetteurs, ainsi que les bits inutilisés pour un nombre pair de chiffres. Le nouveau décodeur doit décider si un remplissage incorrect rejette l'enregistrement, l'envoie en quarantaine ou suit une tolérance historique documentée.
Ce pseudo-code rend la frontière explicite:
decodePacked(bytes, precision, scale, signed):
nibbles = splitEachByte(bytes)
signNibble = nibbles.removeLast()
if precision is even:
require nibbles.removeFirst() == 0
require count(nibbles) == precision
require every nibble is between 0 and 9
sign = decodeSign(signNibble, signed, configuredSignPolicy)
coefficient = sign * decimalDigitsToInteger(nibbles)
return FixedDecimal(coefficient, scale)
Gardez le coefficient sous forme d'entier et l'échelle dans les métadonnées. 123.40 reste ainsi distinct d'une valeur flottante sans échelle, même si l'affichage montre ensuite 123.4. Un encodeur peut aussi reproduire exactement les enregistrements à largeur fixe.
Écrivez l'encodeur séparément, puis testez encode(decode(bytes)) pour chaque entrée canonique valide. Testez aussi les entrées non canoniques acceptées par la politique. L'équivalence numérique peut autoriser la normalisation d'un signe positif F en C, mais la parité binaire échouera si un consommateur attend la forme d'origine. Pour un aller-retour exact, conservez le code de signe initial ou le champ brut complet avec le nombre décodé.
Ne renvoyez pas zéro après une erreur de décodage. Certaines bibliothèques de conversion le font quand l'appelant ignore le statut, transformant une somme mal formée en montant légitime. Retournez un résultat étiqueté qui oblige à traiter les états valide, invalide et différé. Le système de types doit rendre difficile tout calcul accidentel sur des octets non décodés.
Le zéro négatif mérite un test. Une entrée compactée ou zonée peut porter un signe négatif avec uniquement des zéros. La plupart des calculs métier considèrent -0.00 et 0.00 égaux, mais pas forcément un fichier de sortie identique octet par octet, un flux d'audit ou une branche sensible au signe. Décidez si le décodeur normalise, conserve un indicateur de signe ou garde les octets d'origine pour l'aller-retour. L'absence de décision n'est pas une politique.
Le signed overpunch est un autre contrat
Le signed overpunch appartient au décimal zoné ou aux données numériques DISPLAY, pas à COMP-3, même si les deux formats placent un signe dans une position de chiffre. Les confondre corrompt les valeurs tout en produisant des caractères plausibles.
Dans le décimal zoné EBCDIC, chaque chiffre occupe un octet. Avec un overpunch final, le demi-octet haut du dernier chiffre porte le signe et le bas porte le dernier chiffre. Un 123 positif peut finir par une zone positive, tandis que -123 emploie une zone négative. Convertis en caractères selon des tables usuelles, ces octets peuvent apparaître comme des lettres ou des accolades. Cette forme visible est une convention d'encodage, pas la valeur numérique.
La déclaration du champ et l'encodage du fichier doivent rester ensemble. Un analyseur ASCII voyant 12L ne peut pas en déduire sûrement moins trois sans connaître la table d'overpunch utilisée. Une conversion EBCDIC vers Unicode avant le décodage numérique peut aussi détruire les bits de zone ou les transformer en caractères refusés par un analyseur décimal générique.
Décodez dans cet ordre:
- Découpez l'enregistrement selon sa structure binaire avant qu'une conversion de caractères ne change les offsets.
- Appliquez la page de codes EBCDIC déclarée aux champs texte, mais envoyez les octets DISPLAY numériques à un décodeur de décimal zoné.
- Validez chaque zone de chiffre et l'ensemble des signes autorisés.
- Retournez la même représentation coefficient-échelle que pour le décimal compacté.
- Gardez les octets bruts des rejets afin qu'un opérateur identifie le véritable producteur.
SIGN IS LEADING, SIGN IS TRAILING et SIGN IS SEPARATE modifient ce contrat. Un signe séparé occupe sa propre position; un signe overpunch n'en occupe pas. Un analyseur de copybook qui réduit tous les DISPLAY signés à une règle décale les limites des enregistrements ou perd le signe.
Une seule unification est utile: après décodage, décimaux compactés et zonés peuvent partager la même représentation métier. Ils ne doivent pas partager leur décodeur de frontière. Des décodeurs séparés écartent les règles de stockage des calculs et donnent une erreur précise comme invalid packed digit at byte 3 au lieu de number format error.
Un type décimal exact ne suffit pas
Pour l'argent, utilisez des coefficients entiers, des types à virgule fixe ou des nombres décimaux de base de données. Ne faites jamais passer un décimal COBOL par un flottant binaire, même temporairement dans un nombre JSON ou une cellule, car de nombreuses fractions décimales n'ont aucune représentation binaire exacte.
Le type cible dépend des opérations observées et de la plage. S9(7)V99 COMP-3 peut devenir un coefficient signé 64 bits d'échelle deux seulement si tous les produits intermédiaires tiennent. Les valeurs proches de 31 chiffres demandent souvent un entier de précision arbitraire. En base, utilisez DECIMAL(p,s) ou NUMERIC(p,s) après avoir testé arrondis et dépassements. Sur les frontières réseau ou JSON, transmettez une chaîne décimale avec une règle d'échelle claire pour éviter une conversion flottante chez le consommateur.
Go n'a pas de type décimal fixe arbitraire intégré. L'équipe peut stocker les centimes dans un int64 si la preuve complète des calculs confirme la plage, ou encapsuler math/big.Int comme coefficient à échelle contrôlée. Rust peut employer des entiers contrôlés ou une bibliothèque décimale dont précision et arrondis ont été vérifiés. Le number de TypeScript est un flottant binaire; utilisez des chaînes, un bigint mis à l'échelle ou une implémentation décimale testée. numeric dans PostgreSQL est exact, mais l'application décide toujours quand réduire l'échelle.
Prouvez la plage avec les intermédiaires, pas seulement avec les champs stockés. Un montant à neuf chiffres multiplié par un taux à sept chiffres peut demander bien plus de neuf chiffres avant division ou changement d'échelle. COBOL peut conserver cet intermédiaire avec une précision choisie par ses règles et les options du compilateur. Un int64 cible peut contenir chaque entrée et déborder sur leur produit.
Séparez aussi l'échelle de stockage de l'unité métier. PIC S9(7)V99 signifie souvent une devise au centime, mais ne précise ni la devise, ni l'autorisation de fractions de centime pendant le calcul, ni s'il s'agit d'une taxe, d'un taux ou d'un montant. Introduisez ces sens dans les types métier quand le programme les révèle. Money, Rate et Quantity ne doivent pas se combiner simplement parce qu'ils partagent un coefficient décimal.
Ne prenez pas le schéma de base comme première et unique spécification. Une colonne élargie au fil du temps peut accepter ce que le champ COBOL ne stocke pas. Une colonne plus étroite peut révéler qu'une interface arrondissait avant la persistance. Cartographiez déclarations source, instructions, structures de fichiers et contraintes de base dans un même flux numérique.
Définissez les API arithmétiques autour du domaine au lieu d'exposer partout un objet décimal général. Un montant peut s'ajouter à un montant de même unité. Un taux peut multiplier un montant et produire un intermédiaire d'échelle supérieure. Une répartition exige une règle pour le reste, car diviser 10.00 par trois ne donne pas les mêmes centimes à chacun. Ces restrictions révèlent les règles métier qu'une bibliothèque trop permissive laisserait contourner.
La sérialisation a aussi besoin d'un contrat. Décidez si une échelle de deux produit toujours 12.30, si le signe plus est permis, si la notation exponentielle est interdite et combien de chiffres un consommateur accepte. Une chaîne JSON évite la conversion binaire dans votre service, mais n'empêche pas un navigateur, un mappeur de messages ou un chargeur analytique de la convertir ensuite. Les tests de contrat doivent franchir la véritable frontière du consommateur.
L'arrondi se produit aux champs récepteurs
En COBOL, l'arrondi est lié aux instructions arithmétiques et aux champs récepteurs. Reproduire le type final sans chaque changement d'échelle donne d'autres centimes. La présence de ROUNDED constitue un comportement observable.
Prenons ce calcul de taux:
01 WS-BASE PIC S9(7)V99 COMP-3.
01 WS-RATE PIC S9(2)V9(5) COMP-3.
01 WS-FEE PIC S9(7)V99 COMP-3.
COMPUTE WS-FEE ROUNDED = WS-BASE * WS-RATE
Avec WS-BASE = 100.00 et WS-RATE = 0.01255, le produit exact vaut 1.2550000. Le transfert vers une échelle de deux avec un arrondi usuel donne 1.26. Sans ROUNDED, les positions supprimées sont tronquées et le résultat stocké vaut 1.25. Une réécriture utilisant partout l'arrondi pair peut donner 1.26 sur certaines égalités et 1.24 sur d'autres où le mode COBOL choisi s'éloignerait de zéro. Déduisez la règle réelle du compilateur, du dialecte, de l'instruction et des tests.
Ne dispersez pas des appels round(2) dans le code métier traduit. Modélisez le changement d'échelle comme une opération à sémantique nommée:
rescale(value, targetScale, mode)
modes: truncate, nearestAway, nearestEven, floor, ceiling
Annotez ensuite chaque arête du flux reconstitué qui perd de l'échelle. Cela comprend les récepteurs arithmétiques, les MOVE, les appels avec paramètres plus étroits, les affectations en base, les champs de rapport et les enregistrements de sortie. Un MOVE peut supprimer les centimes même si le calcul en conservait quatre positions.
Le signe compte lors de la troncature. Tronquer -1.259 vers zéro donne -1.25; prendre le plancher mathématique donne -1.26. Les langages divergent sur division et reste négatifs. Testez l'implémentation au lieu de supposer qu'une astuce entière se comporte comme COBOL.
Les erreurs de taille font partie du résultat. ON SIZE ERROR peut choisir une branche si le champ récepteur ne contient pas la valeur. D'autres chemins peuvent supprimer des chiffres de tête ou dépendre d'un comportement de compilateur que la cible devrait refuser. Le relevé de parité doit dire si la source a pris la branche d'erreur, pas seulement quel nombre elle a stocké.
Une instruction arithmétique peut avoir plusieurs récepteurs avec des PICTURE différents. COBOL applique le résultat selon la capacité et la clause d'arrondi de chacun. Calculer une fois dans la cible la plus étroite puis copier vers les autres perd l'information plus tôt que la source. Calculez à la précision intermédiaire retrouvée, puis changez séparément l'échelle pour chaque récepteur.
Une devise peut exiger autre chose que deux décimales. Arrondi des espèces, devises sans unité mineure et calculs gardant des fractions de centime existent, mais le copybook ne choisit pas la règle. Retrouvez-la dans les instructions, tables et sorties. N'ajoutez pas une méthode universelle Money.round() en supposant que tous les appels veulent la même chose.
La précision intermédiaire change la réponse
L'ordre d'évaluation, les options arithmétiques du compilateur et les définitions temporaires peuvent changer le dernier centime même avec des décimaux exacts partout. Une arithmétique exacte n'est pas une arithmétique illimitée.
Comparez ces formes:
A = roundToCents(BASE * RATE)
B = roundToCents(roundToScale4(BASE * RATE_PART_1) +
roundToScale4(BASE * RATE_PART_2))
Elles sont liées algébriquement, mais peuvent différer numériquement. La source peut arrondir chaque composant dans un champ de travail avant l'addition. Une réécriture qui fusionne l'expression et n'arrondit qu'une fois modifie le programme. Supprimer les champs de travail COBOL avant d'avoir établi la parité fabrique de petits écarts difficiles à tracer.
La documentation d'IBM Enterprise COBOL distingue ARITH(COMPAT) et ARITH(EXTEND). Le premier limite les opérandes décimaux à 18 chiffres, le second en autorise 31 et change la capacité des intermédiaires fixes. IBM avertit aussi que NUMVAL peut impliquer des approximations et que l'option arithmétique influence le résultat. Un champ décimal au repos peut donc passer par du flottant ou par des intermédiaires de tailles différentes lors d'une conversion.
Inventoriez les réglages du compilateur et de l'environnement pour chaque module. Une même source compilée avec d'autres options ne garantit pas le même contrat exécutable. Conservez ARITH, NUMPROC, TRUNC, le dialecte, la version du compilateur et les paramètres utiles. Si les traces de build manquent, écrivez de petits programmes sondes et exécutez des valeurs limites avec un compilateur compatible avec la production.
Une bonne matrice contient des égalités positives et négatives, les coefficients maximaux, zéro avec chaque signe admis, les produits exigeant un chiffre intermédiaire de plus et une division périodique. Conservez les octets d'entrée, la sortie DISPLAY, les octets de résultat, codes retour et marqueurs de branche. Ces petits programmes règlent plus vite les débats sur ce que COBOL fait "normalement".
Préservez l'ordre d'évaluation dans la première version correcte. Quand la parité reste propre, simplifiez une expression à la fois. Chaque simplification doit prouver qu'elle garde les sorties sur le trafic enregistré et les limites générées.
Les nombres invalides font partie de la migration
Les fichiers de production contiennent souvent des octets contraires aux copybooks, et l'ancien programme peut les tolérer jusqu'à ce qu'une opération force la validation. Une cible qui nettoie silencieusement chaque valeur peut être aussi fausse qu'une cible qui s'arrête au premier enregistrement incorrect.
IBM indique que le compilateur suppose généralement les données conformes à PICTURE et USAGE. NUMCHECK(ZON,PAC) peut ajouter des contrôles quand des champs zonés ou compactés servent d'émetteurs. Les signes valides peuvent dépendre de NUMPROC et de choix d'installation. Deux programmes peuvent donc lire le même enregistrement et exposer l'erreur à des moments différents, selon que l'un compare le champ numériquement et l'autre déplace son groupe sous forme d'octets.
Suivons un échec. Un champ compacté entrant contient 12 34 5A: les demi-octets de chiffres sont valides, mais la politique active refuse le signe. Le traitement nocturne copie tout le groupe vers une archive et réussit, car il ne traite jamais le champ comme un nombre. Plus tard, un total de fin de mois l'emploie comme émetteur, déclenche une exception ou un contrôle et l'envoie dans une file opérateur. Une cible qui décode tout immédiatement le rejette à l'entrée. Une cible qui accepte tous les signes de A à F peut l'additionner. Les deux ont changé le comportement opérationnel.
La bonne réponse est une politique de compatibilité par champ, étayée par des preuves:
- Les champs stricts rejettent immédiatement chiffre, remplissage ou signe invalide.
- Les champs différés gardent les octets bruts et sont décodés à la même frontière sémantique que la source.
- Les encodages tolérés connus ont des cas explicites et des fixtures nommées.
- Les rejets indiquent l'identité du record, le champ, l'offset et l'entrée hexadécimale.
Ne rendez pas la permissivité implicite. Exécutez d'abord la source avec les diagnostics disponibles dans un environnement représentatif, échantillonnez de vrais fichiers et trouvez chaque producteur. Une donnée incorrecte signale souvent une interface non documentée, pas une habitude étrange du mainframe.
Cette distinction change aussi le déploiement. Une différence numérique sur une donnée valide est un défaut de réécriture. Un nouvel enregistrement invalide peut être un défaut source, un défaut du producteur ou une différence de compatibilité choisie. L'équipe a besoin de compteurs et de responsables séparés, sinon le tableau de parité devient un débat autour d'un unique total rouge.
La parité doit comparer plus que les totaux
Un banc de parité doit rejouer les mêmes transactions dans les deux systèmes, puis comparer champs, branches, erreurs et octets sérialisés avant les totaux de lot. Un agrégat égal peut cacher deux erreurs opposées.
Le trafic de production enregistré fournit des combinaisons réalistes, mais rarement les limites. Ajoutez des cas générés pour chaque contrat:
- zéro, zéro négatif, minimum, maximum et une unité hors plage;
- chaque valeur à mi-chemin lors d'une perte d'échelle, des deux côtés de zéro;
- chaque signe compacté ou overpunch accepté et refusé;
- chiffres invalides, remplissage, records courts et erreurs d'encodage;
- valeurs qui débordent seulement après multiplication ou alignement d'échelle.
Pour chaque cas, capturez une enveloppe de comparaison comme celle-ci:
{
"case": "fee-negative-half-cent",
"inputHex": "00001000C000125C",
"source": {
"coefficient": "-126",
"scale": 2,
"status": "OK",
"outputHex": "0000126D"
},
"target": {
"coefficient": "-126",
"scale": 2,
"status": "OK",
"outputHex": "0000126D"
}
}
La forme exacte variera, mais les chaînes pour les coefficients dépassant les entiers sûrs d'un consommateur sont volontaires. Les champs hexadécimaux rendent visibles signes et remplissages. Le statut comprend les branches source comme ON SIZE ERROR, pas seulement le code de sortie du processus.
Si un lot d'un million d'enregistrements diffère d'un centime, divisez d'abord par plage, puis par transaction, champ et opération. Journalisez opérandes non mis à l'échelle, échelles, opération, précision intermédiaire et mode de réduction. Une trace limitée à expected 19.42, got 19.43 laisse tout le travail difficile à une personne au pire moment.
Comparez trois niveaux. La parité numérique contrôle les coefficients après alignement des échelles déclarées. La parité comportementale contrôle décisions, exceptions et records en aval. La parité binaire contrôle les interfaces fixes qui doivent rester identiques. N'exigez pas cette dernière d'une nouvelle API dont le format peut changer sans risque, mais ne vous contentez pas du numérique pour un extrait réglementé lu par offsets.
Traitez les exclusions de comparaison comme du code. Si horodatages, séquences ou formatages volontairement revus diffèrent, normalisez seulement ces champs nommés et examinez la règle comme de la logique de production. Une option générale "ignorer les espaces" peut effacer une position de signe ou un décalage de colonne. Chaque exclusion exige un responsable et une condition d'expiration.
Les critères de livraison doivent séparer les catégories d'écarts, pas afficher un pourcentage mélangé. Zéro différence monétaire inexpliquée est un bon critère même s'il reste des changements d'interface approuvés. Gardez la source rejouable jusqu'à ce que chaque écart possède fixture, décision et test de régression, sinon le même centime reviendra après une optimisation sans rapport.
CodeHero utilise le trafic de production enregistré dans un banc de parité pour cette raison: une traduction qui compile ne prouve pas la conservation du comportement décimal. Pour les grands patrimoines, automatisez catalogue de champs et traces afin que les relecteurs étudient les écarts au lieu de recopier les copybooks.
Le contrat de migration doit être vérifiable
Le contrat numérique fini doit permettre de remonter d'un montant cible jusqu'aux octets source et de suivre chaque frontière d'arrondi. S'il n'existe que dans la tête du dernier mainteneur COBOL, la réécriture n'est pas prête.
Exigez ces éléments avant de basculer un flux monétaire:
- Chaque champ source possède nom qualifié, PICTURE, USAGE, plage d'octets, encodage, précision, échelle et politique de signe.
- Chaque type cible a une preuve écrite de plage qui inclut les intermédiaires.
- Chaque perte d'échelle nomme son arrondi ou sa troncature et l'instruction source qui l'établit.
- Les données invalides ont des fixtures pour les cas acceptés, rejetés, différés et le zéro négatif.
- La suite compare résultats numériques, comportementaux et binaires là où chacun compte.
Conservez ce contrat près du code de remplacement et générez-en autant que possible. Un relecteur doit pouvoir contester S9(11)V9(6) -> int64 avec les plus grands opérandes et voir la réponse, au lieu d'accepter une note "ça tient".
Après la bascule, gardez des métriques distinctes pour erreurs de décodage, dépassements, réductions d'échelle et défauts de parité. N'écrivez ni valeurs de comptes ni records complets dans les journaux généraux. Identifiants de champs et d'opérations, références sûres et coefficients masqués suffisent souvent à localiser une faute sans créer un autre problème de données.
Le standard appliqué à l'argent est volontairement sévère. Chaque centime exige une provenance: chiffres source, interprétation du signe, échelle, opérations intermédiaires et règle finale de changement d'échelle. Quand la cible peut montrer cette chaîne pour une erreur et la prouver sur le corpus, l'ancienne représentation peut disparaître sans emporter son comportement.
FAQ
Qu'est-ce que COMP-3 en COBOL?
COMP-3 est un stockage décimal compacté. Il place deux chiffres dans la plupart des octets et réserve le dernier demi-octet au signe, tandis que PICTURE fournit précision et échelle.
Combien d'octets occupe un champ COMP-3?
Pour n chiffres, comptez floor(n / 2) + 1 octets. Le demi-octet supplémentaire contient le signe et un nombre pair de chiffres laisse un remplissage initial qu'il faut aussi valider.
Le V d'une PICTURE COBOL occupe-t-il un octet?
Non. V marque un séparateur décimal implicite sans stockage; S9(5)V99 stocke donc sept chiffres et un signe avec une échelle de deux.
Peut-on migrer l'argent COBOL vers double ou float?
Pas sans risque. Le flottant binaire ne représente pas exactement de nombreuses fractions décimales, et un passage temporaire peut changer l'arrondi même si la valeur revient ensuite dans un type décimal.
Le signed overpunch est-il identique à COMP-3?
Non. L'overpunch intègre le signe aux bits de zone d'un chiffre DISPLAY, alors que COMP-3 stocke les chiffres dans des demi-octets et réserve le dernier au signe.
Quel demi-octet indique un nombre négatif en décimal compacté?
D est le signe négatif conventionnel, C indique généralement positif et F apparaît souvent pour du non signé. Traitez l'ensemble accepté comme une politique de compilateur et d'interface, car la production peut contenir d'autres codes.
COBOL arrondit-il automatiquement les montants?
Il n'existe pas de règle universelle. L'arrondi dépend de l'instruction, du récepteur et de ROUNDED; sans cette clause, une perte d'échelle tronque généralement.
Pourquoi un décimal exact peut-il encore différer de COBOL?
Les types exacts ont tout de même une précision, un ordre d'évaluation et des règles de changement d'échelle. Modifier un temporaire, fusionner une expression ou arrondir une seule fois peut déplacer le dernier centime.
Comment migrer le zéro négatif?
Décidez s'il faut le normaliser, garder un indicateur de signe ou conserver les octets d'origine pour l'aller-retour. L'égalité numérique ne tranche pas une branche sensible au signe ni une sortie fixe.
Comment prouver qu'une réécriture monétaire COBOL est correcte?
Rejouez les mêmes entrées et comparez coefficients, échelles, branches, erreurs et octets requis. Ajoutez des limites générées, car le trafic enregistré contient rarement chaque égalité, dépassement, signe ou champ mal formé.