Menu ▾ ▴

#1285 0 ** 0 under ON SIZE ERROR still stores 1; with literal operands no size error is raised

GC 3.x
open
nobody
None
5 - default
2026-10-31
2026-10-31
No

0 ** 0 is not treated as a size error in two cases. When both operands are literals, cobc folds it to 1 at compile time and the NOT ON SIZE ERROR phrase runs. When the operands are data items, the SIZE ERROR phrase runs, but the receiving item is still overwritten with 1. The standard says 0 raised to the power 0 is a size error and the receiver must keep its old value.

To reproduce

cfpow0.cob (attached) computes 0 ** 0 on literals:

       IDENTIFICATION DIVISION.
       PROGRAM-ID. CFPOW0.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 R PIC S9(5) VALUE 77.
       PROCEDURE DIVISION.
           COMPUTE R = 0 ** 0
             ON SIZE ERROR DISPLAY "SIZE ERROR, R=" R
             NOT ON SIZE ERROR DISPLAY "NO SIZE ERROR, R=" R
           END-COMPUTE
           STOP RUN.
cobc -std=cobol2014 -x cfpow0.cob && ./cfpow0

sepow0.cob (attached) is the same program with 01 Z PIC S9(5) VALUE 0. added and COMPUTE R = Z ** Z in place of 0 ** 0 (PROGRAM-ID SEPOW0).

What happens

Literal 0 ** 0 is folded to 1 and raises no size error

Output on 3.2.0 (run exit 0):

NO SIZE ERROR, R=+00001

The output is the same with the default dialect (no -std). With -fno-constant-folding added, the output is (run exit 0):

SIZE ERROR, R=+00001

3.3-dev r5751 gives byte-identical output for all three builds (folding, default dialect, -fno-constant-folding), run exit 0.

Run-time 0 ** 0 takes ON SIZE ERROR but still stores 1

Output of cobc -std=cobol2014 -x sepow0.cob && ./sepow0 on 3.2.0 (run exit 0):

SIZE ERROR, R=+00001

The output is the same with the default dialect. 3.3-dev r5751 gives the same output, byte-identical, in both dialects (run exit 0).

What should happen

Both programs should print SIZE ERROR, R=+00077: the size error condition exists, the SIZE ERROR phrase runs, and R keeps 77. ISO/IEC 1989:2023 says this (the 2014 and 2002 editions have the same rules; in their numbering the exponentiation rule is 8.8.1.1):

  • §8.8.1.2: "The following rules apply regardless of the mode of arithmetic that is in effect", and rule 6 a): "If the value of an expression to be raised to a power is zero, the exponent shall have a value greater than zero. Otherwise, the EC-SIZE-EXPONENTIATION exception condition is set to exist and the size error condition is raised."
  • §14.7.5: the size error condition exists "if the rules for evaluation of exponentiation are violated" (case 1). When the SIZE ERROR phrase is specified, rule 1: "If the size error condition occurred during the arithmetic operations specified by the arithmetic statement, the values of all of the resultant data items remain unchanged from the values they had at the start of the execution of the arithmetic statement." Rule 3: "control is transferred to the imperative-statement specified in the SIZE ERROR phrase".

So the fold in the literal case should not happen (or should produce the size error), and in the data-item case the receiving item should not be stored.

Why it happens

In cb_build_binary_op (cobc/tree.c), the constant-folding arm case '^' declines to fold a negative exponent but folds 0 ** 0 to 1. That is the literal case. The run-time case is in cob_decimal_pow, which stores 1 for 0 ^ 0 instead of setting the result to NaN with EC-SIZE-EXPONENTIATION. patches #66 already fixes that part.

Proposed fix

The attached zero-power-zero.patch (against 3.3-dev r5751, applies with patch -p1 at the top of the tree) covers the part patches #66 does not. In cb_build_binary_op (cobc/tree.c), the folding arm case '^' now also declines to fold when both base and exponent are 0, as it already does for a negative exponent. So 0 ** 0 on literals is computed by cob_decimal_pow at run time, like the data-item case. The run-time store of 1 is what patches #66 fixes in cob_decimal_pow, so this patch does not touch libcob.

With this patch alone, cfpow0 takes the same run-time path as sepow0 (cobc -std=cobol2014 -x, default dialect and -fno-constant-folding alike, run exit 0):

SIZE ERROR, R=+00001

With this patch and the intrinsic.c part of patches #66 (which still applies to r5751, hunk 3 at offset 24), both programs give the expected result in all three builds (run exit 0):

SIZE ERROR, R=+00077

make check on 3.3-dev r5751 with the patch (without patches #66): 1373 behaved as expected, 9 skipped, 0 failed, as unpatched (1372) plus the new test 611, which passes; unpatched it prints SIZE ERROR not set from 0 ** 0!. The patch adds the test "COMPUTE 0 ** 0 literal with SIZE ERROR" to tests/testsuite.src/run_fundamental.at (after "Numeric operations (8)"; it also checks that 0 ** 1 and 5 ** 0 still give 0 and 1 without a size error, with and without -fno-constant-folding) and a cobc/ChangeLog entry.

  • #924: the neighbouring case 0 ** (negative exponent), which takes NOT ON SIZE ERROR. It still reproduces on both versions above and is not repeated here.
  • patches #66: one of two pending patches for #924. It also returns NaN for 0 ^ 0 in cob_decimal_pow, which would fix the data-item case.
  • patches #121: the later of the two. It changes only the negative-exponent branch and leaves 0 ^ 0 storing 1. Neither patch touches the constant fold of the literal case.
  • #996: another wrong result from the same constant-folding code (large results).

Details

GnuCOBOL 3.2.0 (Ubuntu gnucobol3 3.2-4build1) and 3.3-dev r5751 (CI snapshot 3.3-dev.138 of 2026-10-29), x86_64 Linux, both re-run 2026-10-30. Both programs are fixed format. Every compile exits 0 with no cobc diagnostic. On 3.2.0 the Ubuntu C compiler step also prints <command-line>: warning: ‘_FORTIFY_SOURCE’ redefined and its note; that is the distribution's build setting.

3 Attachments

Discussion


Log in to post a comment.