1. DISPLAY — a representação padrão
Quando você declara PIC 9(05) sem cláusula USAGE, o campo é DISPLAY — cada dígito ocupa exatamente um byte, gravado como caractere EBCDIC (no mainframe) ou ASCII (em ambiente PC/Linux). É o único formato que pode aparecer diretamente em arquivos sequenciais e em tela sem conversão.
WORKING-STORAGE SECTION. 01 WS-VALOR PIC 9(05). *DISPLAY implícito — 5 bytes 01 WS-VALOR-X PIC 9(05) DISPLAY. *Explícito — idêntico * Para o valor 12345, a memória contém (EBCDIC): * F1 F2 F3 F4 F5 (cada dígito = 0xFn) 01 WS-SIGNED S9(05). * Sinal no nibble alto do último byte: * +12345 → F1 F2 F3 F4 C5 (C = positivo) * -12345 → F1 F2 F3 F4 D5 (D = negativo) 01 WS-NOME X(20). *Alfanumérico — sempre DISPLAY
✅ Quando usar DISPLAY
- Campos lidos/gravados em arquivos sequenciais (QSAM, VSAM com registros texto)
- Campos exibidos em tela CICS sem formatação extra
- Campos de chave VSAM KSDS (devem ser DISPLAY para comparação direta)
- Qualquer campo alfanumérico
PIC X
2. COMP-3 — Packed Decimal
COMP-3 (também chamado PACKED-DECIMAL) empacota dois dígitos por byte. O último nibble (meio-byte) carrega o sinal: C para positivo, D para negativo, F para não-sinalizado. É o tipo mais usado em COBOL bancário para valores monetários — ocupa metade do espaço de DISPLAY e a UCP do mainframe faz aritmética diretamente em packed via instruções AP/SP/MP/DP, sem converter para binário.
WORKING-STORAGE SECTION. 01 WS-SALDO S9(13)V99 COMP-3. * 13 dígitos inteiros + 2 decimais implícitos = 15 dígitos * Tamanho: CEIL((15+1)/2) = 8 bytes * Fórmula: CEIL((n_digitos + 1) / 2) 01 WS-TAXA S9(03)V9(06) COMP-3. * 9 dígitos total → 5 bytes 01 WS-QTDE S9(07) COMP-3. * 7 dígitos → 4 bytes PROCEDURE DIVISION. MOVE 123456789 TO WS-QTDE * Na memória: 01 23 45 67 8C * ── ── ── ── ── * 01 23 45 67 = dígitos empacotados * 8C = último dígito (8) + sinal positivo (C) COMPUTE WS-SALDO = WS-SALDO * 1.0253 ADD 100 TO WS-SALDO
🦕 Analogia — dois passageiros por assento
Imagine que cada byte é um assento de avião com duas poltronas (nibbles). No DISPLAY, apenas uma poltrona é usada — a outra carrega sempre o prefixo F. No COMP-3, as duas poltronas são ocupadas com dígitos de verdade. Com 8 bytes, DISPLAY guarda 8 dígitos; COMP-3 guarda 15. O avião está lotado do jeito certo.
Calculando o tamanho em bytes
Bytes = CEIL( (total_de_dígitos + 1) / 2 ) PIC S9(07) → CEIL( (7+1)/2 ) = 4 bytes PIC S9(09) → CEIL( (9+1)/2 ) = 5 bytes PIC S9(13)V99 → CEIL((15+1)/2 ) = 8 bytes PIC S9(15) → CEIL((15+1)/2 ) = 8 bytes PIC S9(17) → CEIL((17+1)/2 ) = 9 bytes ← máximo prático
⚠️ S0C7 — o abend do COMP-3 não inicializado
Se um campo COMP-3 contém X'00' (não inicializado) e você tenta usá-lo em aritmética ou MOVE para campo numérico, o z/OS gera S0C7 (data exception). Sempre use INITIALIZE ou MOVE ZEROS antes de operar sobre campos COMP-3. Ver artigo Abends e Debugging.
3. COMP / COMP-4 — Binário puro
COMP e COMP-4 são sinônimos. O campo é armazenado em binário big-endian (byte mais significativo primeiro, conforme padrão z/Architecture). O compilador aloca 2 bytes para campos de até 4 dígitos e 4 bytes para campos de 5 a 9 dígitos — independente de quantos dígitos você declarou dentro do limite.
WORKING-STORAGE SECTION. * 2 bytes — guarda de -32.768 a +32.767 (signed) 01 WS-IDX S9(04) COMP. *subscript de tabela pequena 01 WS-CONTADOR S9(04) COMP. *contador de loop * 4 bytes — guarda de -2.147.483.648 a +2.147.483.647 01 WS-LINHAS S9(09) COMP. *contagem de registros 01 WS-OCORRE-MAX S9(09) COMP. *limite de OCCURS DEPENDING ON * 8 bytes — até 18 dígitos (declara S9(10) a S9(18)) 01 WS-BIG S9(18) COMP. PROCEDURE DIVISION. PERFORM VARYING WS-IDX FROM 1 BY 1 UNTIL WS-IDX > 100 CONTINUE END-PERFORM * WS-IDX em COMP: incremento PERFORM sem conversão = rápido
✅ Quando usar COMP
- Subscripts e índices de
OCCURS— o compilador gera código mais enxuto - Contadores de loop e acumuladores de quantidade sem casas decimais
- Campos de interface com rotinas C ou assembler que esperam inteiro binário
OCCURS DEPENDING ON— o campo ODO obrigatoriamente deve serCOMP
COMP não trunca como COMP-3
01 WS-SMALL S9(04) COMP. *2 bytes, max +32.767 PROCEDURE DIVISION. MOVE 40000 TO WS-SMALL * PIC 9(04) comportaria apenas 9999, mas COMP aloca 2 bytes * O compilador pode truncar pelo PIC ou permitir overflow * binário (wrap-around) — comportamento depende de TRUNC * Opção de compilação TRUNC(BIN) — não trunca pelo PIC, * usa capacidade total do campo binário * Opção TRUNC(STD) — trunca pelo PIC (padrão ANSI) * Opção TRUNC(OPT) — compilador decide o mais rápido
🟣 Opção de compilação TRUNC
A maioria dos shops define TRUNC(OPT) ou TRUNC(BIN) no JCL de compilação. Com TRUNC(BIN), um campo PIC S9(04) COMP pode guardar até 32.767 mesmo que a PIC sugira 9.999. Isso é comum em código legado — sempre confirme a opção usada no seu ambiente antes de assumir que o limite é o PIC.
4. COMP-5 — Native Binary
COMP-5 é binário como COMP, mas usa a ordem de bytes nativa do hardware. No mainframe z/Architecture isso é big-endian — igual a COMP. A diferença aparece em ambiente PC/Linux (little-endian): um campo COMP gravado no mainframe e lido num servidor Intel teria os bytes trocados, gerando valor errado. Com COMP-5, o compilador garante que vai usar a ordem correta para a plataforma.
WORKING-STORAGE SECTION. * Campos passados para rotinas C em ambiente Linux/PC 01 WS-INT32 S9(09) COMP-5. *int em C — 4 bytes, native 01 WS-INT16 S9(04) COMP-5. *short em C — 2 bytes, native 01 WS-INT64 S9(18) COMP-5. *long em C — 8 bytes, native * No mainframe z/OS: COMP-5 == COMP (ambos big-endian) * Em GnuCOBOL (PC/Linux): COMP-5 gera little-endian * COMP gera big-endian (diferente!)
💗 No dia a dia do mainframe
Se você trabalha exclusivamente em z/OS e não faz interface com programas C ou Java rodando em servidor separado, pode usar COMP e COMP-5 de forma intercambiável — os dois geram big-endian. A distinção importa quando você está escrevendo código portável ou enviando dados binários para sistemas fora do mainframe.
5. COMP-1 e COMP-2 — Ponto flutuante
COMP-1 é ponto flutuante de precisão simples (4 bytes, equivalente ao float de C). COMP-2 é precisão dupla (8 bytes, equivalente a double). Eles não aceitam cláusula PIC — o tamanho é fixo e determinado pelo tipo.
WORKING-STORAGE SECTION. 01 WS-FLOAT COMP-1. *sem PIC — 4 bytes (~7 dígitos significativos) 01 WS-DOUBLE COMP-2. *sem PIC — 8 bytes (~15 dígitos significativos) PROCEDURE DIVISION. MOVE 3.14159265358979 TO WS-DOUBLE COMPUTE WS-DOUBLE = WS-DOUBLE * 2.0 DISPLAY WS-DOUBLE *→ 6.28318530717958 * Funções matemáticas usam COMP-2: COMPUTE WS-DOUBLE = FUNCTION SQRT(2) COMPUTE WS-DOUBLE = FUNCTION LOG(WS-DOUBLE) COMPUTE WS-DOUBLE = FUNCTION SIN(WS-DOUBLE)
⚠️ NUNCA use COMP-1/COMP-2 para valores monetários
Ponto flutuante binário não consegue representar exatamente a maioria das frações decimais. O valor 0.10 (dez centavos) em COMP-1 é aproximadamente 0.10000000149.... Em cálculos repetidos (como juros compostos de milhões de contas), o erro acumula e gera centavos divergentes — o pesadelo de qualquer auditor. Para dinheiro, use sempre COMP-3 com casas decimais explícitas na PIC.
✅ Quando COMP-1/COMP-2 fazem sentido
- Cálculos científicos ou estatísticos onde precisão relativa importa, não exata
- Interface com funções intrínsecas matemáticas (SQRT, SIN, LOG, EXP)
- Passagem de parâmetros para rotinas C que esperam
float/double - Campos recebidos de sensores ou sistemas externos com ponto flutuante nativo
6. Tabela comparativa
| Tipo | Sinônimo | Representação | Tamanho | PIC obrigatória | Uso principal |
|---|---|---|---|---|---|
DISPLAY |
(padrão) | 1 byte/dígito (EBCDIC/ASCII) | n bytes | Sim | Arquivos, tela, chaves VSAM |
COMP-3 |
PACKED-DECIMAL | 2 dígitos/byte + nibble de sinal | ⌈(n+1)/2⌉ bytes | Sim | Valores monetários, totais |
COMP |
COMP-4 / BINARY | Binário big-endian | 2, 4 ou 8 bytes | Sim | Subscripts, contadores, ODO |
COMP-5 |
NATIVE-BINARY | Binário nativo (big ou little) | 2, 4 ou 8 bytes | Sim | Interop C/Java em multipla plataforma |
COMP-1 |
FLOAT-SHORT | IEEE 754 simples (float) | 4 bytes fixos | Não | Cálculos científicos |
COMP-2 |
FLOAT-LONG | IEEE 754 dupla (double) | 8 bytes fixos | Não | Funções intrínsecas matemáticas |
Comparação de espaço para o mesmo valor
Tipo Bytes Observação ────────── ────── ──────────────────────────────────────────── DISPLAY 15 1 byte por dígito (15 dígitos ao total) COMP-3 8 ⌈(15+1)/2⌉ = 8 bytes — 47% menor que DISPLAY COMP 8 PIC S9(13) → 8 bytes (entra na faixa 10–18) COMP-1 4 mas só ~7 dígitos significativos — INACEITÁVEL COMP-2 8 mas arredondamento binário — INACEITÁVEL p/ $
7. Como escolher o tipo certo
Caso de uso Tipo recomendado ─────────────────────────────────────── ───────────────── Campo em registro de arquivo sequencial DISPLAY Chave de pesquisa VSAM KSDS DISPLAY Campo exibido em tela CICS DISPLAY Valor monetário / taxa / saldo COMP-3 Subtotal / total acumulado COMP-3 Subscript de tabela OCCURS COMP (S9(04) ou S9(09)) Campo ODO (OCCURS DEPENDING ON) COMP Contador de registros lidos/gravados COMP Interface com C/assembler (z/OS) COMP ou COMP-5 Interface com Java/C em servidor Linux COMP-5 Função SQRT, SIN, LOG, EXP COMP-2 Campo recebido de sistema com float COMP-2
💗 Regra de bolso para iniciantes
Dinheiro → COMP-3. Contador/subscript → COMP. Arquivo/tela → DISPLAY. Se você memorizar só isso, vai cobrir 95% dos casos encontrados em sistemas bancários COBOL no dia a dia.
8. Conversões e armadilhas
O compilador converte automaticamente entre os tipos quando você faz MOVE ou aritmética entre campos de tipos diferentes. Mas algumas conversões têm custo ou perigo:
WORKING-STORAGE SECTION. 01 WS-DISP S9(07). 01 WS-PACK S9(07) COMP-3. 01 WS-BIN S9(09) COMP. PROCEDURE DIVISION. * O compilador gera a conversão necessária em cada MOVE: MOVE WS-DISP TO WS-PACK *DISPLAY → COMP-3: OK, gerado CVB/PACK MOVE WS-PACK TO WS-BIN *COMP-3 → COMP: OK, gerado UNPK/CVB MOVE WS-BIN TO WS-DISP *COMP → DISPLAY: OK, gerado CVD/UNPK * ARITMÉTICA mista — o compilador escolhe o tipo interno COMPUTE WS-PACK = WS-PACK + WS-BIN * Internamente: converte WS-BIN para packed, soma em packed * Mais eficiente manter os dois como COMP-3 se soma é frequente
Reinterpretação de bytes com REDEFINES
REDEFINES sobrepõe o mesmo espaço de memória com uma PIC diferente — sem conversão. Isso é útil para inspecionar o conteúdo bruto de um campo binário, mas perigoso se os tipos são incompatíveis:
WORKING-STORAGE SECTION. 01 WS-VALOR-PACK S9(07) COMP-3. *4 bytes 01 WS-VALOR-HEX X(04) REDEFINES WS-VALOR-PACK. PROCEDURE DIVISION. MOVE 1234567 TO WS-VALOR-PACK DISPLAY 'HEX: ' WS-VALOR-HEX * Exibe os 4 bytes brutos: 01 23 45 6C * 01 23 45 = dígitos 6C = último dígito (6) + sinal positivo (C) * ⚠️ NUNCA faça aritmética em WS-VALOR-HEX — * ele é X, não numérico. Use só para DISPLAY de diagnóstico.
Alinhamento de estruturas com SYNCHRONIZED
01 WS-REGISTRO. 05 WS-FLAG X(01). 05 WS-CONTA S9(09) COMP SYNCHRONIZED. * Sem SYNC: WS-CONTA começa no byte 2 — não alinhado em word * Com SYNC: compilador insere 3 bytes de padding para alinhar * WS-CONTA no próximo limite de doubleword (byte 4) * No z/Architecture, COMP não alinhado ainda funciona (sem exceção), * mas SYNC pode melhorar performance em loops intensos.
9. Exemplo completo
O sub-programa CALCJURO recebe capital, taxa mensal e prazo, calcula juros compostos e devolve o montante final e os juros devidos. Demonstra o uso correto de cada tipo de dado no mesmo programa:
*----------------------------------------------------------------
* CALCJURO — calcula montante por juros compostos
* Interface: CAPITAL IN, TAXA-MES IN, PRAZO-MESES IN
* MONTANTE OUT, JUROS OUT
*----------------------------------------------------------------
IDENTIFICATION DIVISION.
PROGRAM-ID. CALCJURO.
DATA DIVISION.
WORKING-STORAGE SECTION.
* Acumulador interno — COMP-2 para cálculo científico (potência)
01 WS-FATOR COMP-2. *(1 + taxa)^prazo — ponto flutuante
01 WS-TAXA-PERC COMP-2. *taxa convertida p/ decimal (ex: 0.0253)
01 WS-RESULT-FLT COMP-2. *resultado em float antes de arredondar
* Contador do loop — COMP
01 WS-MES-ATUAL S9(04) COMP.
LINKAGE SECTION.
01 LK-CAPITAL S9(13)V99 COMP-3. *moeda — COMP-3
01 LK-TAXA-MES S9(03)V9(06) COMP-3. *ex: 0.025300
01 LK-PRAZO S9(04) COMP. *contador — COMP
01 LK-MONTANTE S9(13)V99 COMP-3. *resultado — COMP-3
01 LK-JUROS S9(13)V99 COMP-3. *resultado — COMP-3
PROCEDURE DIVISION USING
LK-CAPITAL LK-TAXA-MES LK-PRAZO
LK-MONTANTE LK-JUROS.
0000-PRINCIPAL.
* Converte taxa COMP-3 → COMP-2 para usar FUNCTION POWER
MOVE LK-TAXA-MES TO WS-TAXA-PERC
COMPUTE WS-FATOR =
FUNCTION POWER(1.0 + WS-TAXA-PERC, LK-PRAZO)
* Resultado em float → converte de volta para COMP-3
COMPUTE WS-RESULT-FLT =
LK-CAPITAL * WS-FATOR
* Arredonda para 2 casas e grava em COMP-3
COMPUTE LK-MONTANTE ROUNDED = WS-RESULT-FLT
COMPUTE LK-JUROS = LK-MONTANTE - LK-CAPITAL
GOBACK.
🦕 A estratégia de "ponte flutuante"
O exemplo usa COMP-2 apenas como ponte interna para a função POWER (que precisa de ponto flutuante), e depois converte o resultado de volta para COMP-3 com ROUNDED. Isso garante que o resultado final seja exato na segunda casa decimal — o mundo do float fica contido dentro do sub-programa e não vaza para o chamador.
10. Erros comuns
1. COMP-1/COMP-2 para campo monetário
* ❌ ERRADO — ponto flutuante perde centavos 01 WS-SALDO-ERRO COMP-1. PROCEDURE DIVISION. MOVE 1000000.10 TO WS-SALDO-ERRO DISPLAY WS-SALDO-ERRO *→ 1000000.12 (!!!) — imprecisão de float * ✅ CORRETO — COMP-3 preserva exatamente os centavos 01 WS-SALDO-OK S9(13)V99 COMP-3. MOVE 1000000.10 TO WS-SALDO-OK DISPLAY WS-SALDO-OK *→ 100000010 (com V99 implícito = 1000000.10)
2. COMP-3 sem inicialização → S0C7
01 WS-TOTAL S9(11)V99 COMP-3. *conteúdo inicial: X'00..00' PROCEDURE DIVISION. * ❌ ADD sem inicializar → S0C7 ADD WS-VALOR TO WS-TOTAL * ✅ Inicializa antes de acumular INITIALIZE WS-TOTAL ADD WS-VALOR TO WS-TOTAL
3. Usar COMP em campo de arquivo
* ❌ Se o arquivo é texto fixo (DISPLAY), declarar COMP lê lixo FD ARQ-CLIENTES RECORD CONTAINS 100 CHARACTERS. 01 REG-CLI. 05 CLI-SALDO S9(13)V99 COMP-3. * Se o arquivo foi gravado em DISPLAY, os 8 bytes lidos * como COMP-3 resultam em S0C7 ou valor absurdo. * ✅ Use o mesmo tipo com que o arquivo foi gravado. * Se vier de outro sistema, consulte o copybook de layout.
4. S9(05) COMP guardando valor > 9999 sem TRUNC(BIN)
01 WS-CNT S9(04) COMP. *2 bytes = até 32.767 em binário * = até 9.999 pela PIC PROCEDURE DIVISION. MOVE 15000 TO WS-CNT * Com TRUNC(STD): WS-CNT = 5000 (truncou pelo PIC = 4 dígitos) * Com TRUNC(BIN): WS-CNT = 15000 (usa capacidade do binário) * Solução segura: declarar PIC compatível com o valor esperado * S9(09) COMP para até 2.147.483.647 sem ambiguidade
5. REDEFINES misturando DISPLAY e COMP-3
01 WS-CAMPO-X X(08). 01 WS-CAMPO-NUM S9(13)V99 COMP-3 REDEFINES WS-CAMPO-X. * ⚠️ Tamanhos COINCIDEM (8 bytes), mas se WS-CAMPO-X * contém texto EBCDIC (F1 F2...), lido como COMP-3 * gerará S0C7 ao tentar aritmética. * Use REDEFINES de COMP-3 sobre X apenas para diagnóstico, * nunca para operação de produção.