https://gcc.gnu.org/bugzilla/show_bug.cgi?id=126821

            Bug ID: 126821
           Summary: [cobol] DISPLAY of a P-scaled item renders the scaling
                    positions as digits
           Product: gcc
           Version: 17.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: cobol
          Assignee: unassigned at gcc dot gnu.org
          Reporter: peeterjoot at protonmail dot com
  Target Milestone: ---

Created attachment 65319
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65319&action=edit
pscale-display-padded.md (as described in report)

# Summary

`DISPLAY` of a numeric item whose PICTURE contains a `P` run emits one
character per
scaling position, so the output is wider than the item. `PIC 99PPP` displays as
five
characters where the item is two bytes.

A `P` position is a *scaling* position, not a stored digit: it is not part of
the item,
and outside four named operations it is not counted in the size of an operand
either.
DISPLAY is not one of those four — it transfers an operand's contents — so a
`PIC 99PPP`
item displays as its two stored characters.

The defect looks confined to DISPLAY. gcobol already lays these items out at
the right
size and already truncates and rounds them at the right position: it passes the
NIST
COBOL 85 tests that assert both (see *Corroboration* below). The item is stored
correctly
and then printed as though the scaling positions were part of it.

# Environment

- Compiler: `gcobol (Ubuntu 15.2.0-16ubuntu1) 15.2.0` (GCC COBOL front end)
- Also reproduces unchanged on a trunk build dated 2026-08-03.
- Platform: Linux x86-64.
- No special compiler options.

# Reproducer

`pscale-display-padded.cob` (attached):

```cobol
       01  CTRL   PIC 99.
       01  TRAIL  PIC 99PPP.
       01  LEAD   PIC PP99.
       ...
           MOVE 20 TO CTRL
           DISPLAY "CTRL  [" CTRL "]"
           MOVE 20000 TO TRAIL
           DISPLAY "TRAIL [" TRAIL "]"
           MOVE 0.0012 TO LEAD
           DISPLAY "LEAD  [" LEAD "]"
```

```
gcobol -o pscale pscale-display-padded.cob && ./pscale
```

`CTRL` is the control — an unsigned USAGE DISPLAY item with no `P` run. `TRAIL`
and
`LEAD` differ from it only in having one, and both hold a **representable**
value (a
multiple of 1000 for `TRAIL`, of 0.0001 for `LEAD`), so what is stored is not
in question:
only how many characters DISPLAY emits for it.

# Observed

```
CTRL  [20]
TRAIL [20000]
LEAD  [.0012]
```

# Expected

```
CTRL  [20]
TRAIL [20]
LEAD  [12]
```

`CTRL` already agrees. `TRAIL` gains three `0` characters, one per scaling
position, and
`LEAD` gains two plus an editing decimal point.

# Corroboration: the NIST COBOL 85 validation suite

`NC202A` and `NC253A` both assert the storage size of a `P`-scaled item, in a
form that
does not depend on how any implementation chooses to display one. Each declares
a group
whose fifth member is `PICTURE 9P`:

```cobol
       01  GRP-FOR-ADD-CORR-R.
           02  GRP-SUBTRACT-CORR-1.
           05  ADD-CORR-1              PICTURE 99.
           05  ADD-CORR-2              PICTURE 99.
           05  ADD-CORR-3              PICTURE 99.
           05  ADD-CORR-4              PICTURE 99.
           05  ADD-CORR-5              PICTURE 9P.
           05  ADD-CORR-6              PICTURE 999.
           05  ADD-CORR-7              PICTURE 99.
           05  ADD-CORR-8              PICTURE 99.
           05  ADD-CORR-9              PICTURE 99.
           05  FILLER                  PICTURE 99.
```

and then compares the whole group — a byte-wise alphanumeric comparison —
against a
literal of a fixed length, after an `ADD CORRESPONDING` that sends `55` to
`ADD-CORR-5`:

```cobol
           ADD CORRESPONDING GRP-FOR-ADD-CORR-1 TO GRP-FOR-ADD-CORR-R.
           IF GRP-FOR-ADD-CORR-R EQUAL TO "11223344506677889900"
```

The literal is **20** characters, and it lines up only if `PICTURE 9P` occupies
**one**
byte:

| member | PICTURE | bytes | contributes |
|---|---|---|---|
| ADD-CORR-1 | `99` | 2 | `11` |
| ADD-CORR-2 | `99` | 2 | `22` |
| ADD-CORR-3 | `99` | 2 | `33` |
| ADD-CORR-4 | `99` | 2 | `44` |
| **ADD-CORR-5** | **`9P`** | **1** | **`5`** |
| ADD-CORR-6 | `999` | 3 | `066` |
| ADD-CORR-7 | `99` | 2 | `77` |
| ADD-CORR-8 | `99` | 2 | `88` |
| ADD-CORR-9 | `99` | 2 | `99` |
| FILLER | `99` | 2 | `00` |
| | | **20** | |

Give the `P` a character of its own and the group is 21 bytes and the assertion
cannot
hold at any value.

The same pair of tests also pins the *value* the scaling position stands for.
`55` added
to a zeroed `PIC 9P` leaves the stored digit `5` — the item's value being digit
× 10, that
is 50, the sending 55 truncated relative to the rightmost position for which
storage
exists. The immediately following test repeats the identical `ADD
CORRESPONDING` with
`ROUNDED` and asserts `"11223344606677889900"` — the same layout with the digit
`6`, i.e.
60. Truncating and rounding at the tens is only meaningful if the units
position is a
scaling position that holds nothing.

The suite is public: it is distributed by NIST as `newcob.val`, and GnuCOBOL's
own
testsuite carries the same programs.

**gcobol passes both of these.** Compiling and running `NC202A` gives "NO
TEST(S) FAILED",
with `ADD-TEST-F3-1` and `ADD-TEST-F3-2` — the two paragraphs above — reported
as `PASS`:

```
 ADD CORRESPONDING    PASS  ADD-TEST-F3-1
 ADD CORRESPONDING    PASS  ADD-TEST-F3-2
```

So gcobol already lays a `P`-scaled item out at the right size and already
truncates and
rounds at the rightmost position for which storage exists. The defect is
confined to
DISPLAY: the item is stored correctly and then printed as though the scaling
positions
were part of it.

Note what the NIST programs do and do not cover. They establish the *size* of a
`P`-scaled
item directly, which is the rule this report turns on; they do not `DISPLAY`
one — they
write their results to a report file. The step from size to DISPLAY is the
DISPLAY
statement's own definition, below.

# Reference

IBM Enterprise COBOL for z/OS 6.5 Language Reference, SC27-8713-04, **P
symbol** (p.215)
lists the only operations that use a `P` item's algebraic value:

> "In certain operations that reference a data item whose PICTURE 
> character-string
> contains the symbol P, the algebraic value of the data item is used rather 
> than the
> actual character representation of the data item. […] These operations are 
> any of the
> following ones:
> • Any operation that requires a numeric sending operand
> • A MOVE statement where the sending operand is numeric and its PICTURE 
> character-string
> contains the symbol P
> • A MOVE statement where the sending operand is numeric-edited and its PICTURE
> character-string contains the symbol P, and the receiving operand is numeric 
> or
> numeric-edited
> • A comparison operation where both operands are numeric
>
> In all other operations, the digit positions specified with the symbol P are 
> ignored and
> are **not counted in the size of the operand**."

All four are sending-side or comparison. The PICTURE symbol table on p.213 says
the same
of the item itself: "Not counted in the size of the data item."

**DISPLAY statement** (p.340) describes what is transferred:

> "The DISPLAY statement transfers the contents of each operand to the output 
> device."

and enumerates the conversions it performs — binary and internal decimal to
zoned,
internal floating-point to external floating-point, `USAGE POINTER` to `PIC
9(10)` —
closing with "No other categories of data require conversion." A `USAGE
DISPLAY` item is
already zoned, so its *contents* are transferred: the stored digit bytes, of
which a `P`
position is not one.

ISO/IEC 1989:2023 agrees, in the same words. 13.18.40.4 carries the identical
"In all other operations … are not counted in the size of the operand" sentence
and the
same four-operation list, and adds of the item itself: "The symbol 'P' is not
counted in
the size of the item, but each symbol 'P' is counted in the maximum number of
digit
positions." 14.7.4.1 states the arithmetic rule the NIST tests above exercise:
"When the
low-order integer positions in a resultant identifier are represented by the
symbol P in
the picture character-string for that resultant identifier, rounding or
truncation occurs
relative to the rightmost integer position for which storage is allocated."

One qualification, since ISO's DISPLAY does delegate a good deal. 14.9.11.4 GR1
says "any
conversion of data required between … the data item referenced by identifier-1
and the
device is defined by the implementor" — so where a conversion *is* required, as
for a
binary or packed item, the rendering is the implementor's choice. That is not
this case:
the items here are `USAGE DISPLAY`, already in character form, so no conversion
is
required and GR1 has nothing to delegate. What applies instead is GR1's first
sentence,
"the content of each operand", and GR4, "the data item is transferred" — and
the content
and size of a `P`-scaled item are exactly what 13.18.40.4 and the NIST tests
above pin.

# Impact

DISPLAY output for any `P`-scaled item is wider than a reference listing
produced by a
conforming implementation, which matters when diffing against captured output.
`P` runs
are rare in new code but common in the older programs that motivate a COBOL
port.

Reply via email to