Ir para o conteúdo
14 de ago. de 2026·8 min de leitura

Migrar COBOL COMP-3 sem perder um centavo

Uma migração COBOL COMP-3 falha se escala, sinais, arredondamento ou bytes inválidos mudarem. Modele o contrato e confira cada valor.

Migrar COBOL COMP-3 sem perder um centavo

Uma reescrita de sistema monetário tem um único critério de aceitação: a mesma entrada válida deve produzir o mesmo valor, sinal, estado e representação armazenada sempre que essa representação continuar fazendo parte de uma interface. Uma diferença de um centavo não é um defeito cosmético. Em um milhão de registros, ela pode mudar o total do razão, uma fila de exceções, uma faixa de juros ou o arquivo aceito por um programa posterior.

A suposição perigosa é que um campo COBOL corresponde a um tipo de uma linguagem moderna. Não corresponde. Seu significado vem de PICTURE, USAGE, opções do compilador, instruções aritméticas, campos receptores, layout de arquivo e, às vezes, décadas de dados incorretos tolerados. PIC S9(7)V99 COMP-3 descreve um coeficiente inteiro com sinal, escala implícita de dois e um contrato de armazenamento compactado. Tratá-lo como número genérico descarta pelo menos metade dessas informações.

Já vi equipes discutirem decimal contra double por mais tempo do que levaram para rastrear o MOVE que realmente eliminava casas decimais. Essa discussão começa tarde demais. Primeiro recupere o contrato numérico. Depois escolha uma representação de destino capaz de aplicá-lo e execute os dois sistemas com o mesmo tráfego até explicar cada diferença.

A cláusula PICTURE faz parte do valor

A cláusula PICTURE informa a quantidade de dígitos, a escala, a possibilidade de sinal e, às vezes, posições de escala que não ocupam armazenamento. Nada disso é apenas formatação.

Considere estas declarações:

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 é um ponto decimal implícito. Não existe byte de ponto decimal na memória nem no disco. INVOICE-AMOUNT armazena nove dígitos decimais e um sinal, e o coeficiente 123456789 significa 1234567.89. TAX-RATE usa escala quatro. WHOLE-DOLLARS tem escala zero. SMALL-RATIO não tem posições inteiras, portanto o coeficiente 123456 significa 0.123456.

Um inventário de migração deve registrar pelo menos (signed, precision, scale, usage, byte length) para cada item numérico elementar. Guarde também a declaração original e o layout do registro que o contém. Copybooks usam REDEFINES, OCCURS, nomes de condição e movimentos de grupo, então nem sempre um campo pode ser interpretado sem considerar os bytes vizinhos.

O símbolo P exige tratamento separado. Ele descreve posições de escala implícitas que não são armazenadas. A documentação do IBM Enterprise COBOL traz exemplos como PPP999, cujos dígitos armazenados representam valores de zero a .000999, e S999PPP, cujos valores diferentes de zero avançam em milhares. Um mapeador que conta apenas os dígitos armazenados perde a escala aritmética. Não deduza um DECIMAL(p,s) do tamanho em bytes.

Produza um catálogo legível por máquina em vez de uma planilha que acabará divergindo do código. Uma entrada útil tem esta forma:

{
  "qualifiedName": "CLAIM-REC.PAID-AMOUNT",
  "picture": "S9(7)V99",
  "usage": "COMP-3",
  "bytes": 5,
  "precision": 9,
  "scale": 2,
  "signed": true,
  "storage": "packed-decimal"
}

Esse artefato inicia o contrato da reescrita. Ele também detecta um erro comum do analisador: nove dígitos em decimal compactado precisam de cinco bytes porque o último meio byte contém o sinal.

Resolva os aliases antes de definir o significado. Um ramo REDEFINES pode tratar os mesmos cinco bytes como valor em um tipo de transação e como preenchimento ou data em outro. O discriminador que seleciona o ramo faz parte do contrato numérico. Se a nova camada de entrada decodificar imediatamente todos os ramos possíveis, poderá rejeitar registros válidos porque bytes numéricos em um layout são texto em outro. Registre a condição controladora, não apenas os offsets sobrepostos.

As operações de grupo merecem atenção pelo motivo oposto. MOVE OLD-GROUP TO NEW-GROUP copia bytes sem aplicar regras de conversão numérica aos itens. Substituí-lo por mapeamento campo a campo pode normalizar sinais, mudar preenchimento ou decodificar um campo que a origem nunca examinava. Classifique cada uso como operação de bytes ou operação numérica antes de declarar equivalente um objeto tipado.

Bytes COMP-3 precisam de decodificador

O decimal compactado guarda dois dígitos por byte, exceto no nibble inferior do último byte, que carrega o sinal. O ambiente de destino precisa validar e decodificar explicitamente esses nibbles em cada fronteira externa.

Para PIC S9(5)V99 COMP-3, o valor -12345.67, com coeficiente -1234567, pode aparecer assim:

12 34 56 7D

Leia os nibbles como 1 2 3 4 5 6 7 D. Os sete primeiros são dígitos. O D final indica negativo. Um valor positivo convencional termina em C; dados compactados sem sinal costumam terminar em F. Sistemas reais podem conter outros códigos, conforme opções do compilador e o caminho que produziu os dados. Por isso o decodificador precisa de uma política declarada, não de uma conversão hexadecimal permissiva.

O tamanho em bytes para n dígitos armazenados é floor(n / 2) + 1. Quando a quantidade de dígitos é par, o primeiro nibble é preenchimento. Ele também importa na validação. A IBM documenta que NUMCHECK(PAC) verifica dígitos e sinais compactados quando os campos atuam como emissores e, para uma quantidade par, também verifica bits não usados. O novo decodificador deve decidir se um preenchimento incorreto rejeita o registro, o envia à quarentena ou reproduz uma tolerância antiga explicitamente documentada.

Este pseudocódigo deixa a fronteira visível:

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)

Mantenha o coeficiente como inteiro e a escala como metadado. Assim, 123.40 permanece diferente de um valor de ponto flutuante sem escala, mesmo se a tela depois mostrar 123.4. Isso também permite que um codificador reproduza exatamente registros de largura fixa.

Escreva o codificador separadamente e depois teste encode(decode(bytes)) para toda entrada canônica válida. Teste também as entradas não canônicas aceitas pela política. A equivalência numérica pode permitir normalizar um sinal positivo F para C, mas a paridade de bytes falhará se um consumidor esperar a forma original. Quando a ida e volta exata importar, mantenha o código de sinal original ou o campo bruto completo ao lado do número decodificado.

Não deixe o decodificador retornar zero após um erro. Algumas bibliotecas fazem isso quando ninguém verifica o estado da conversão, transformando dinheiro malformado em um valor legítimo. Retorne um resultado marcado que obrigue o chamador a tratar os estados válido, inválido e adiado. O sistema de tipos deve dificultar cálculos acidentais com bytes não decodificados.

O zero negativo merece um teste. Uma entrada compactada ou zonada pode conter sinal negativo com todos os dígitos iguais a zero. A maioria dos cálculos comerciais considera -0.00 e 0.00 numericamente iguais, mas um arquivo de saída idêntico byte a byte, um fluxo de auditoria ou um ramo sensível ao sinal talvez não. Decida se a decodificação normaliza, mantém um indicador de sinal ou preserva os bytes originais para a volta. A ausência de decisão não é uma política.

Signed overpunch é outro contrato

Signed overpunch pertence ao decimal zonado ou a dados numéricos DISPLAY, não a COMP-3, embora os dois formatos comprimam um sinal em uma posição de dígito. Confundi-los corrompe valores e ainda produz caracteres que parecem plausíveis.

No decimal zonado EBCDIC, cada dígito ocupa um byte. Com overpunch no final, o nibble superior do último byte de dígito contém o sinal e o inferior contém o último dígito. Um 123 positivo pode terminar com uma zona positiva, enquanto -123 usa uma zona negativa. Convertidos em caracteres por tabelas conhecidas, esses bytes podem aparecer como letras ou chaves. Essa forma visual é uma convenção de codificação, não o valor numérico.

A declaração do campo e a codificação do arquivo precisam permanecer juntas. Um analisador ASCII que recebe 12L não pode deduzir com segurança menos três sem saber qual tabela de overpunch o produziu. Uma conversão de EBCDIC para Unicode antes da decodificação numérica também pode destruir os bits de zona ou mapeá-los para caracteres rejeitados por um analisador decimal genérico.

Decodifique nesta ordem:

  1. Divida o registro pelo layout de bytes antes que a conversão de caracteres altere os offsets.
  2. Aplique a página EBCDIC declarada aos campos de texto, mas envie bytes DISPLAY numéricos a um decodificador de decimal zonado.
  3. Valide cada zona de dígito e o conjunto permitido de sinais.
  4. Retorne a mesma representação de coeficiente e escala usada para decimal compactado.
  5. Guarde os bytes brutos dos registros rejeitados para que um operador identifique o produtor real.

SIGN IS LEADING, SIGN IS TRAILING e SIGN IS SEPARATE mudam o contrato. Um sinal separado ocupa sua própria posição; um sinal overpunch não. Analisadores de copybook que reduzem todos os DISPLAY com sinal a uma única regra deslocam limites do registro ou perdem o sinal.

Há uma unificação útil: depois da decodificação, decimal compactado e zonado podem compartilhar a mesma representação de domínio. Eles não devem compartilhar o decodificador de fronteira. Decodificadores separados mantêm regras de armazenamento fora dos cálculos e fornecem erros precisos, como invalid packed digit at byte 3, em vez de number format error.

Tipos decimais exatos não são suficientes

Use coeficientes inteiros, tipos de ponto fixo ou numéricos do banco para dinheiro. Nunca passe um decimal COBOL por ponto flutuante binário, mesmo temporariamente como número JSON ou célula de planilha, pois muitas frações decimais não têm representação binária exata.

O mapeamento de destino deve seguir as operações observadas e o intervalo. S9(7)V99 COMP-3 pode usar um coeficiente de 64 bits com sinal e escala dois somente depois de provar que os produtos intermediários cabem. Valores próximos de 31 dígitos normalmente exigem inteiro de precisão arbitrária. No banco, use DECIMAL(p,s) ou NUMERIC(p,s) depois de testar arredondamento e estouro. Em fronteiras de rede ou JSON, transporte strings decimais com regras explícitas de escala para impedir conversão para ponto flutuante.

Go não tem decimal de ponto fixo e precisão arbitrária embutido. A equipe pode guardar centavos em int64 se o cálculo completo provar o intervalo, ou encapsular math/big.Int como coeficiente com escala controlada. Rust pode usar aritmética inteira verificada ou biblioteca decimal cuja precisão e modos de arredondamento tenham sido auditados. O number do TypeScript é ponto flutuante binário; para dinheiro, use strings, bigint escalado ou implementação decimal testada. numeric no PostgreSQL é exato, mas a aplicação ainda controla quando reduzir a escala.

Prove o intervalo com intermediários, não apenas campos armazenados. Um valor de nove dígitos multiplicado por uma taxa de sete pode precisar de muito mais do que nove antes da divisão ou mudança de escala. COBOL pode manter esse intermediário com precisão definida pelas regras aritméticas e opções do compilador. Um int64 no destino pode conter cada entrada e mesmo assim estourar no produto.

Também separe escala de armazenamento de unidade comercial. PIC S9(7)V99 costuma significar moeda em centavos, mas não informa qual moeda, se frações de centavo são permitidas durante o cálculo, nem se o valor é imposto, taxa ou montante. Coloque esses significados em tipos de domínio quando o programa os revelar. Money, Rate e Quantity não devem se combinar só porque todos usam coeficiente decimal.

Não use o esquema do banco como primeira e única especificação. Uma coluna ampliada ao longo dos anos pode aceitar valores que não cabem no campo COBOL. Uma coluna mais estreita pode revelar que uma interface já arredondava antes de persistir. Mapeie declarações, instruções, layouts e restrições do banco como um único fluxo numérico.

Defina APIs aritméticas ao redor do domínio, em vez de expor um objeto decimal genérico em toda parte. Um valor pode somar outro da mesma unidade. Uma taxa pode multiplicar um valor e produzir um intermediário de escala maior. Uma divisão precisa de regra para o resto, pois repartir 10.00 entre três não dá os mesmos centavos a todos. Essas restrições expõem regras comerciais que uma biblioteca permissiva deixaria contornar.

A serialização também precisa de contrato. Decida se escala dois sempre gera 12.30, se sinais positivos são permitidos, se notação exponencial é proibida e quantos dígitos um consumidor aceita. Uma string JSON evita conversão binária no seu serviço, mas não impede que navegador, mapeador de mensagens ou carregador analítico a converta depois. Testes de contrato devem atravessar a fronteira real do consumidor.

O arredondamento ocorre nos campos receptores

Encontre mudanças ocultas de escala
A análise completa segue dinheiro por MOVE, campos de trabalho, arquivos e bancos.

COBOL liga arredondamento a instruções aritméticas e campos receptores. Reproduzir o tipo final sem reproduzir cada transição de escala resulta em outros centavos. A presença ou ausência de ROUNDED é comportamento observável.

Considere um cálculo de taxa:

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

Com WS-BASE = 100.00 e WS-RATE = 0.01255, o produto exato é 1.2550000. Passar para escala dois com arredondamento comum ao mais próximo produz 1.26. Sem ROUNDED, as posições descartadas são truncadas e o resultado armazenado é 1.25. Uma reescrita que use sempre arredondamento para o par pode gerar 1.26 em alguns empates e 1.24 em outros nos quais o modo COBOL escolhido se afastaria de zero. Obtenha a regra real do compilador, dialeto, instrução e testes.

Não espalhe chamadas a round(2) pelo código comercial traduzido. Modele a mudança de escala como operação com semântica nomeada:

rescale(value, targetScale, mode)
modes: truncate, nearestAway, nearestEven, floor, ceiling

Anote cada aresta do fluxo recuperado que perde escala. Isso inclui receptores aritméticos, MOVE, chamadas com parâmetros mais estreitos, atribuições no banco, campos de relatório e registros de saída. Um MOVE pode eliminar centavos mesmo que o cálculo preserve quatro casas.

O sinal importa ao truncar. Truncar -1.259 em direção a zero produz -1.25; usar piso matemático produz -1.26. Linguagens discordam sobre divisão e resto negativos, portanto teste a implementação em vez de supor que um atalho inteiro age como COBOL.

Erros de tamanho também fazem parte do resultado. ON SIZE ERROR pode escolher um ramo quando o receptor não comporta o valor. Outros caminhos podem cortar dígitos superiores ou depender de comportamento do compilador que o destino deveria rejeitar. O registro de paridade deve dizer se a origem tomou esse ramo, não apenas o número armazenado.

Uma instrução aritmética pode ter vários receptores com PICTURE diferentes. COBOL aplica o resultado conforme a capacidade e a cláusula de arredondamento de cada um. Calcular uma vez no receptor mais estreito e copiar para os demais perde informação mais cedo que a origem. Calcule com a precisão intermediária recuperada e mude a escala separadamente em cada receptor.

Regras monetárias podem exigir algo além de duas casas. Arredondamento de dinheiro físico, moedas sem unidade menor e cálculos com frações de centavo existem, mas o copybook não escolhe uma política. Recupere a regra de instruções, tabelas e saídas. Não crie um Money.round() universal presumindo que todas as chamadas querem a mesma resposta.

A precisão intermediária muda o resultado

Ordem de avaliação, opções aritméticas do compilador e campos temporários podem mudar o último centavo mesmo quando origem e destino usam decimais exatos. Aritmética exata não significa aritmética ilimitada.

Compare estas formas:

A = roundToCents(BASE * RATE)
B = roundToCents(roundToScale4(BASE * RATE_PART_1) +
                 roundToScale4(BASE * RATE_PART_2))

Elas são relacionadas pela álgebra, mas não precisam ser numericamente iguais. A origem pode arredondar cada componente para um campo de trabalho antes de somar. Uma reescrita que una a expressão e arredonde apenas uma vez muda o programa. Eliminar campos de trabalho COBOL antes de estabelecer paridade produz diferenças pequenas e difíceis de rastrear.

A documentação do IBM Enterprise COBOL distingue ARITH(COMPAT) e ARITH(EXTEND). A primeira limita operandos decimais a 18 dígitos; a segunda permite 31 e muda a capacidade dos intermediários de ponto fixo. A IBM também alerta que NUMVAL pode envolver valores aproximados e que a opção aritmética afeta resultados. Um campo decimal em repouso pode passar por ponto flutuante ou intermediários de outros tamanhos durante a conversão.

Inventarie as configurações de compilador e execução de cada módulo. A mesma fonte compilada com opções diferentes não garante o mesmo contrato executável. Registre ARITH, NUMPROC, TRUNC, dialeto, versão e ajustes relevantes. Se faltarem dados de build, crie programas sonda e execute entradas limite com um compilador compatível com produção.

Uma matriz útil inclui empates positivos e negativos, coeficientes máximos, zero com cada sinal aceito, produtos que precisam de um dígito intermediário extra e divisões periódicas. Guarde bytes de entrada, saída DISPLAY, bytes resultantes, códigos de retorno e marcas de ramo. Esses programas pequenos encerram mais rápido discussões sobre o que COBOL faz "normalmente".

Preserve a ordem de avaliação na primeira versão correta. Quando a paridade permanecer limpa, simplifique uma expressão por vez. Cada mudança deve provar que mantém as saídas no tráfego gravado e nos limites gerados.

Dados numéricos inválidos também fazem parte

Reescreva além da aritmética
CodeHero lê em paralelo todas as linguagens, tarefas e interfaces ao redor do COBOL.

Arquivos de produção muitas vezes contêm bytes que violam os copybooks, e o programa antigo pode tolerá-los até uma operação forçar validação. Uma reescrita que limpe cada valor silenciosamente pode estar tão errada quanto uma que falhe no primeiro registro sujo.

A IBM afirma que o compilador normalmente pressupõe dados compatíveis com PICTURE e USAGE. NUMCHECK(ZON,PAC) pode adicionar verificações quando campos zonados ou compactados atuam como emissores. Sinais válidos podem depender de NUMPROC e de opções da instalação. Dois programas podem ler o mesmo registro e expor o defeito em pontos diferentes, pois um compara o campo numericamente e o outro move o grupo como bytes.

Siga uma falha. Um campo compactado de entrada contém 12 34 5A: nibbles de dígitos válidos, mas um sinal rejeitado pela política ativa. O processo noturno copia o grupo inteiro para um arquivo e termina porque nunca trata o campo como número. Depois, um total de fechamento mensal usa o campo como emissor, dispara uma exceção ou verificação e envia o registro à fila do operador. Uma reescrita que decodifica tudo na entrada o rejeita cedo. Outra que aceita sinais de A a F pode somá-lo. Ambas alteraram o comportamento operacional.

A resposta correta é uma política de compatibilidade por campo, sustentada por evidências:

  • Campos estritos rejeitam imediatamente dígito, preenchimento ou sinal inválido.
  • Campos adiados conservam bytes brutos e são decodificados na mesma fronteira semântica da origem.
  • Codificações toleradas conhecidas recebem casos explícitos e fixtures nomeadas.
  • Registros rejeitados levam identidade, campo, offset e entrada hexadecimal.

Não faça da permissividade o padrão. Primeiro execute a origem com diagnósticos disponíveis em ambiente representativo, amostre arquivos reais e localize cada produtor. Dados sujos costumam revelar uma interface não documentada, não um hábito peculiar do mainframe.

Essa distinção também muda a implantação. Uma diferença numérica em dados válidos é defeito da reescrita. Um registro inválido recém-detectado pode ser defeito do dado, do produtor ou diferença deliberada de compatibilidade. A equipe precisa de contadores e responsáveis separados, ou o painel de paridade vira discussão sobre um único total vermelho.

A paridade precisa comparar mais que totais

Explique os bytes de sinal
O harness expõe sinais compactados, overpunch, bytes de saída e diferenças entre ramos.

Um harness de paridade deve reproduzir transações idênticas na origem e no destino, depois comparar campos, ramos, erros e bytes serializados antes dos totais de lote. Um agregado igual pode esconder dois erros opostos.

Tráfego de produção gravado oferece combinações realistas, mas raramente cobre limites numéricos. Adicione casos gerados para cada contrato:

  • zero, zero negativo, mínimo, máximo e uma unidade fora do intervalo;
  • cada valor de empate em toda perda de escala, nos dois lados de zero;
  • cada sinal compactado ou overpunch aceito e rejeitado;
  • dígitos inválidos, preenchimento, registros curtos e erros de codificação;
  • valores que só estouram depois da multiplicação ou alinhamento de escala.

Para cada caso, capture um envelope de comparação como este:

{
  "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"
  }
}

O formato exato varia, mas usar strings para coeficientes que excedem o inteiro seguro do consumidor é intencional. Campos hexadecimais mostram diferenças de sinal e preenchimento. O estado inclui ramos da origem como ON SIZE ERROR, não só o código de saída do processo.

Se um lote de um milhão de registros diferir em um centavo, divida primeiro por faixa, depois por transação, campo e operação. Registre operandos sem escala, escalas, operação, precisão intermediária e modo de ajuste. Uma trilha que mostra apenas expected 19.42, got 19.43 deixa o trabalho difícil para uma pessoa no pior momento.

Execute três níveis de comparação. Paridade numérica confere coeficientes depois de alinhar escalas declaradas. Paridade comportamental confere decisões, exceções e registros posteriores. Paridade de bytes confere interfaces fixas que devem continuar idênticas. Não exija esta última de uma nova API cuja formatação possa mudar com segurança, nem aceite apenas a numérica em um extrato regulado lido por offsets.

Trate exclusões de comparação como código. Se timestamps, sequências ou formatos redesenhados diferirem, normalize somente esses campos e revise a regra como lógica de produção. Uma opção ampla de "ignorar espaços" pode apagar posição de sinal ou deslocamento de coluna. Cada exclusão precisa de responsável e condição de expiração.

Portões de liberação devem relatar classes de diferença, não uma porcentagem combinada. Zero diferenças monetárias sem explicação é um bom portão mesmo quando restam mudanças de interface aprovadas. Mantenha a origem reproduzível até cada diferença ter fixture, decisão e teste de regressão, ou o mesmo centavo voltará após uma otimização não relacionada.

CodeHero usa tráfego de produção gravado em um harness de paridade por esse motivo: uma tradução que compila não prova que o comportamento decimal sobreviveu. Em sistemas grandes, automatize catálogo de campos e geração de trilhas para que revisores analisem diferenças em vez de transcrever copybooks.

O contrato de migração deve ser revisável

O contrato numérico final deve permitir rastrear qualquer valor de destino até os bytes de origem e adiante por cada fronteira de arredondamento. Se ele só existe na cabeça do último mantenedor COBOL, a reescrita não está pronta.

Exija estes itens antes de trocar um fluxo monetário:

  1. Cada campo numérico de origem tem nome qualificado, PICTURE, USAGE, faixa de bytes, codificação, precisão, escala e política de sinal.
  2. Cada tipo de destino tem uma prova escrita de intervalo que inclui intermediários.
  3. Cada perda de escala nomeia seu arredondamento ou truncamento e a instrução de origem que o definiu.
  4. Dados inválidos têm fixtures para casos aceitos, rejeitados, adiados e zero negativo.
  5. A suíte compara resultados numéricos, comportamentais e de bytes onde cada um importa.

Guarde esse contrato ao lado do código novo e gere automaticamente o máximo possível. Um revisor deve poder contestar S9(11)V9(6) -> int64 com os maiores operandos e ver a resposta, não aceitar uma nota dizendo "cabe".

Depois da troca, mantenha métricas distintas para falhas de decodificação, estouros, reduções de escala e diferenças de paridade. Não grave valores de contas nem registros completos em logs gerais. Identificadores de campo e operação, referências seguras e coeficientes ocultos normalmente bastam para localizar a falha sem criar outro problema de dados.

O padrão para dinheiro é deliberadamente severo. Cada centavo precisa de procedência: dígitos de origem, interpretação do sinal, escala, operações intermediárias e regra final de ajuste. Quando a reescrita consegue mostrar essa cadeia para uma falha e prová-la no conjunto de dados, a representação antiga pode desaparecer sem levar seu comportamento.

Perguntas frequentes

O que é COMP-3 em COBOL?

COMP-3 é armazenamento decimal compactado. Ele coloca dois dígitos na maioria dos bytes e reserva o último meio byte para o sinal, enquanto PICTURE fornece precisão e escala.

Quantos bytes um campo COMP-3 usa?

Para n dígitos, use floor(n / 2) + 1 bytes. O meio byte adicional contém o sinal, e uma quantidade par deixa um nibble inicial de preenchimento que também deve ser validado.

O V em uma PICTURE COBOL ocupa um byte?

Não. V marca um ponto decimal implícito sem armazenamento, portanto S9(5)V99 guarda sete dígitos e um sinal com escala dois.

Dinheiro em COBOL pode ser migrado para double ou float?

Não com segurança. Ponto flutuante binário não representa exatamente muitas frações decimais, e uma conversão temporária pode mudar o arredondamento mesmo que o valor volte a um tipo decimal.

Signed overpunch é igual a COMP-3?

Não. Overpunch incorpora o sinal nos bits de zona de um dígito DISPLAY, enquanto COMP-3 armazena dígitos em nibbles e reserva o último para o sinal.

Qual nibble indica negativo no decimal compactado?

D é o sinal negativo convencional, C costuma indicar positivo e F aparece em dados sem sinal. Trate o conjunto aceito como política do compilador e da interface, pois produção pode conter outros códigos.

COBOL arredonda valores monetários automaticamente?

Não há regra universal. Depende da instrução, do receptor e da presença de ROUNDED; sem essa cláusula, a perda de escala normalmente trunca.

Por que um decimal exato ainda pode diferir do COBOL?

Tipos exatos ainda têm precisão, ordem de avaliação e regras de mudança de escala. Alterar um temporário, unir expressões ou arredondar uma vez pode mover o último centavo.

Como migrar o zero negativo?

Decida se vai normalizá-lo, manter um indicador de sinal ou preservar os bytes para a volta. Igualdade numérica não resolve um ramo sensível ao sinal nem uma saída fixa.

O que prova que uma reescrita monetária COBOL está correta?

Reproduza as mesmas entradas e compare coeficientes, escalas, ramos, erros e bytes necessários. Adicione limites gerados porque o tráfego gravado raramente contém cada empate, estouro, sinal ou campo malformado.