GnuCOBOL -std=ibm and other dialects: what each one changes
What cobc -std=ibm actually switches in GnuCOBOL 3.2, read from its dialect files, and one program whose output changes when the dialect does.
GnuCOBOL can compile the same source as if it were written for several other compilers, and -std is the switch that picks which one. Most people meet it as -std=ibm, because they have a program from a z/OS shop and want it to compile on a laptop. This lesson answers two questions about that switch: what does -std=ibm change, setting by setting, and does it change what a compiled program prints? It does. The program at the end of the lesson prints different numbers under the default dialect and under -std=ibm, from the same source and the same data.
What was run and what was not. cobc is not installed on the machine this lesson was prepared on, so the COBOL listing has not been compiled. Everything below was read from the GnuCOBOL 3.2 release, downloaded from the GNU project's server on October 28, 2026, where 3.2 is still the newest release listed: the dialect files in its config folder, the compiler and runtime source, and its test suite. The settings tables were printed by the Python companion, /code/gnucobol-std-dialects-ibm.py, which reads the dialect files the way the 3.2 compiler reads them; those are real runs. The program output further down is the expected output, worked out by the same companion from the 3.2 source; its rules pass 15 checks, six of which compare them with whole cases from GnuCOBOL's own test suite. It is not a captured run. If a compiled run ever disagrees with it, the compiler wins and this page is wrong.
What -std actually does
-std=NAME makes cobc load one file, NAME.conf, from its configuration folder. The 3.2 compiler builds the file name with nothing more than sprintf (ext, "%s.conf", cob_optarg) in cobc/cobc.c, then reads it. The Programmer's Guide (10.1.5) says the folder defaults to /usr/local/share/gnucobol/config on Unix-like systems unless COB_CONFIG_DIR says otherwise; cobc --info prints the folder your compiler uses.
The 3.2 help text (cobc/help.c) lists 19 names: default, cobol2014, cobol2002, cobol85, xopen, ibm-strict, ibm, mvs-strict, mvs, mf-strict, mf, bs2000-strict, bs2000, acu-strict, acu, rm-strict, rm, gcos-strict and gcos. The folder also holds realia-strict.conf and realia.conf, which the help text does not mention but the 3.2 test suite compiles with. The copy of the help text in the Programmer's Guide comes from an earlier build and stops at rm.
Each vendor comes in two flavours. The strict file describes the vendor's compiler as closely as GnuCOBOL can. The plain name is a lax variant, and ibm.conf is only three lines of substance:
include "ibm-strict.conf"
include: "ibm.words"
include "lax.conf-inc"
The note at the top of lax.conf-inc says what the third line is for: it allows "features that the original compilers don't support (larger lengths, syntax variations,...)". So -std=ibm is IBM's behaviour with GnuCOBOL's tolerance, and -std=ibm-strict is IBM's behaviour with IBM's limits.
Three reading rules from cobc/config.c explain the rest. A later line overrides an earlier one, so whatever lax.conf-inc sets wins over ibm-strict.conf. A support value written with a plus sign, such as +obsolete, only relaxes: the reader applies it when the value already in force is stricter, and never over skip or ignore. And any -f option on the command line is applied after the file. cobc reads its options twice, loading -std and -conf files in the first pass and applying -f settings in the second, so -fbinary-truncate wins over the dialect file wherever it sits on the command line. The test suite checks exactly that, with the comment "configuration flag on command line must override all (no matter where it's used)".
The settings that differ
This is the companion's table for the settings that change how data is stored, how a running program behaves, or how strict the compiler is, in five dialects. It is a real run against the 3.2 config folder:
setting default ibm-strict ibm mvs mf
binary-size 1-2-4-8 2-4-8 2-4-8 2-4-8 1--8
binary-truncate yes no no no no
binary-byteorder big-endian big-endian big-endian big-endian big-endian
pretty-display yes no no no yes
move-ibm no yes yes yes no
perform-osvs no yes yes yes no
arithmetic-osvs no yes yes yes no
sticky-linkage no yes yes yes no
hostsign no yes yes yes no
defaultbyte init 0 0 0 " "
init-justify no yes yes yes no
odoslide no yes yes yes no
assign-clause dynamic external external external dynamic
filename-mapping yes yes yes yes yes
subscript-check full max max max max
format auto cobol85 cobol85 cobol85 fixed
reserved-words default IBM default default default
word-length 63 30 63 63 63
literal-length 8191 160 8192 8192 8192
pic-length 255 50 255 255 255
relax-syntax-checks no no yes yes yes
complex-odo no yes yes yes yes
indirect-redefines no yes yes yes yes
larger-redefines error error warning warning ok
The descriptions below are the compiler's own, from cobc/config.def in 3.2:
| Setting | 3.2's description | default | ibm |
|---|---|---|---|
| binary-size | "binary byte size - defines the allocated bytes according to PIC" | 1-2-4-8 | 2-4-8 |
| binary-truncate | "numeric truncation according to ANSI" | yes | no |
| pretty-display | "alternate formatting of numeric fields" | yes | no |
| move-ibm | "MOVE operates as on IBM (left to right, byte by byte)" | no | yes |
| perform-osvs | "exit point of any currently executing perform is recognized if reached" | no | yes |
| arithmetic-osvs | "limit precision in intermediate results to precision of final result (less accurate)" | no | yes |
| sticky-linkage | "LINKAGE SECTION items remain allocated between invocations" | no | yes |
| hostsign | "allow hexadecimal value 'F' for NUMERIC test of signed PACKED DECIMAL field" | no | yes |
| defaultbyte | "default initialization for fields without VALUE" | init | 0 |
| init-justify | "applies JUSTIFY with VALUE clause" | no | yes |
Two rows deserve a second look before any program is moved. defaultbyte is init in the default dialect, which the same description defines as "initialize to PICTURE/USAGE", so a numeric item with no VALUE starts at zero. Under -std=ibm it is 0, which the description lists as a "decimal 0..255 representing a character": every byte of an item without VALUE starts as binary zero, and a PIC 9 field full of binary zeros is not a valid number. A program that adds to a counter it never initialised can work under the default dialect and fail under -std=ibm. The other is format: the default is auto, and the IBM files set cobol85, the fixed card layout with Area A checking, so a free-format source needs -free or a >>SOURCE FORMAT IS FREE line when it is compiled with -std=ibm. Our listing starts with that line.
The reserved words
The reserved-word list changes too, and here strict and lax really part. ibm-strict.conf says reserved-words: IBM, which makes the compiler use ibm.words as the complete list; the file's own header says that in this case "the default reserved word list will not be used". ibm.conf includes the same file instead, and lax.conf-inc then sets reserved-words: default, so the IBM entries are added to GnuCOBOL's full built-in list. The companion's --words option prints it:
-std=default built-in default list
-std=ibm-strict ibm.words only (462 entries)
-std=ibm built-in default list + 462 entries from ibm.words
The test suite has the case that shows the difference. A copybook-style record using USAGE BINARY-LONG, a GnuCOBOL word IBM does not have, fails under the strict dialect and compiles under the lax one. The expected messages, from syn_misc.at:
| Command | Result in the 3.2 test suite |
|---|---|
cobc -fsyntax-only -std=ibm-strict prog.cob |
error: 'BINARY-LONG' is not defined, but is a reserved word in another dialect |
cobc -fsyntax-only -std=ibm-strict -freserved=BINARY-LONG prog.cob |
compiles |
cobc -fsyntax-only -std=ibm prog.cob |
compiles |
To see the list your own compiler uses, the Programmer's Guide points to cobc --list-reserved (2.1.1, where it adds that the list is "subject to variation depending on usage of the --std line command"), and its Appendix C1 gives the per-dialect form, cobc -std=dialect --list-reserved.
One paragraph, every dialect
Dialects also decide how loudly the compiler complains about old syntax. The 3.2 test suite compiles a short program with an AUTHOR. paragraph in the IDENTIFICATION DIVISION under each -std. These are its expected results, copied from configuration.at:
| -std | Result |
|---|---|
| default | warning: AUTHOR is obsolete in GnuCOBOL |
| cobol85 | warning: AUTHOR is obsolete in COBOL 85 |
| cobol2002 | error: AUTHOR does not conform to COBOL 2002 |
| cobol2014 | error: AUTHOR does not conform to COBOL 2014 |
| bs2000-strict | error: AUTHOR does not conform to BS2000 COBOL |
| ibm-strict | warning: AUTHOR is obsolete in IBM COBOL |
| mvs-strict | warning: AUTHOR is obsolete in IBM COBOL for MVS & VM |
| mf-strict | warning: AUTHOR is obsolete in Micro Focus COBOL |
| acu-strict | warning: AUTHOR is obsolete in ACUCOBOL-GT |
| rm-strict | warning: AUTHOR is obsolete in RM-COBOL |
| gcos-strict | warning: AUTHOR is obsolete in GCOS |
| xopen | warning: AUTHOR used |
| realia-strict | no message |
| ibm, mf, acu, rm, mvs, gcos, bs2000, realia | no message |
The lax dialects are silent because lax.conf-inc sets comment-paragraphs: ok.
A program that prints different numbers
The listing, /code/gnucobol-std-dialects-ibm.cob, declares four USAGE COMP items, which GnuCOBOL treats as USAGE BINARY:
01 WS-Tiny PIC 9(2) COMP VALUE 12.
01 WS-Count PIC 9(4) COMP VALUE 5000.
01 WS-Delta PIC S9(4) COMP VALUE -12.
01 WS-Total PIC 9(5) COMP VALUE 12345.
For each one it prints the size in bytes and the value as DISPLAY shows it. FUNCTION LENGTH of an item that is not national returns its size in bytes (cob_intr_length in libcob/intrinsic.c returns srcfield->size):
MOVE FUNCTION LENGTH(WS-Tiny) TO WS-Bytes
DISPLAY "WS-TINY PIC 9(2) COMP BYTES " WS-Bytes
" SHOWS " WS-Tiny
Then it adds 7000 to the 5000 in WS-Count. The answer, 12000, has five digits and the PICTURE allows four:
ADD 7000 TO WS-Count
DISPLAY "ADD 7000 TO WS-COUNT SHOWS " WS-Count
Compiled with plain cobc -x, the 3.2 source says it prints:
WS-TINY PIC 9(2) COMP BYTES 01 SHOWS 12
WS-COUNT PIC 9(4) COMP BYTES 02 SHOWS 5000
WS-DELTA PIC S9(4) COMP BYTES 02 SHOWS -0012
WS-TOTAL PIC 9(5) COMP BYTES 04 SHOWS 12345
ADD 7000 TO WS-COUNT SHOWS 2000
WS-COUNT IS 2000
Compiled with cobc -x -std=ibm, the same source should print:
WS-TINY PIC 9(2) COMP BYTES 02 SHOWS 00012
WS-COUNT PIC 9(4) COMP BYTES 02 SHOWS 05000
WS-DELTA PIC S9(4) COMP BYTES 02 SHOWS -00012
WS-TOTAL PIC 9(5) COMP BYTES 04 SHOWS 0000012345
ADD 7000 TO WS-COUNT SHOWS 12000
WS-COUNT IS 12000
Every line differs, and three settings from the table account for all of it.
binary-size decides the bytes. Under 1-2-4-8 a COMP item with one or two digits gets one byte, three or four digits get two, five to nine get four. Under 2-4-8 the smallest size is two bytes. That is the table in the comment of every dialect file, and it is what compute_binary_size in cobc/field.c does. Only WS-Tiny changes size here, from one byte to two, but a record layout with small COMP fields changes length when the dialect changes, and every field after them moves.
pretty-display decides how DISPLAY prints a binary item. With it on, libcob/termio.c edits the value into a picture of the item's own digits, with a leading sign if the item is signed: four digits for PIC 9(4), so 5000 prints as 5000 and -12 as -0012. With it off, the runtime prints the value with as many digits as the item's bytes can hold. The widths come from a table in termio.c, bin_digits[] = { 1, 3, 5, 8, 10, 13, 15, 17, 20 }, indexed by the size in bytes: five digits for two bytes, ten for four. A signed item always gets a sign, plus or minus, because the format is %+*.*lld (%+*.*I64d in Windows builds). That is where 05000, -00012 and 0000012345 come from. The test suite's "BINARY: full-print" case, which turns pretty-display off, expects 001 for a one-digit item in one byte and -001 for its signed twin, the same rule at a smaller size.
binary-truncate decides what the ADD keeps. With truncation on and no ON SIZE ERROR phrase, cobc/typeck.c compiles the ADD with the option it labels "Truncate binary field to digits in picture", and the runtime keeps the remainder after dividing by 10 to the power of the digit count (mpz_tdiv_r in cob_decimal_get_binary): 12000 leaves 2000. With truncation off, the only limit is the two bytes, which hold up to 65535 unsigned, so 12000 is stored whole. The EVALUATE after the ADD shows that this is the stored value and not a display effect. The test suite's own case, "Numeric MOVE with/without -fbinary-truncate", moves, computes and adds 30000 into a PIC 9(4) COMP item and expects "x IS ZERO" three times by default and "x IS 30000" three times with -fno-binary-truncate.
A program that relies on truncation, such as a counter that is meant to wrap at 9999, or one that relies on its absence, such as a binary field that routinely holds more digits than its PICTURE declares, gives different answers under the two dialects with no message at compile time. The ON SIZE ERROR lesson shows the portable way out: test for the overflow instead of relying on what the store does with it.
Overriding one setting
The command-line rule above means a dialect can be adjusted one setting at a time. cobc -x -std=ibm -fbinary-truncate keeps IBM's sizes and display and puts truncation back:
WS-TINY PIC 9(2) COMP BYTES 02 SHOWS 00012
WS-COUNT PIC 9(4) COMP BYTES 02 SHOWS 05000
WS-DELTA PIC S9(4) COMP BYTES 02 SHOWS -00012
WS-TOTAL PIC 9(5) COMP BYTES 04 SHOWS 0000012345
ADD 7000 TO WS-COUNT SHOWS 02000
WS-COUNT IS 2000
When a project needs several changes, a dialect file of its own is tidier than a long command line. The test suite's own example of -conf is a file that includes default.conf, sets a name, and changes one setting; the same shape built on the IBM dialect would be:
include "ibm.conf"
name: "IBM rules with truncation"
binary-truncate: yes
Saved as shop.conf and used with cobc -x -conf=shop.conf, it should give the output above. The two options look for files differently (cb_load_conf and cb_load_conf_file in config.c): -std always reads from the configuration folder, while -conf takes the name as a path, and an include of a bare file name is looked for in the working folder first, then in the folder of the file that includes it, then in the configuration folder. Use -conf instead of -std, not together with it: loading a second file discards the first, and the test suite expects the warning "The previous loaded configuration 'GnuCOBOL' will be discarded." when both are given.
The dialect a program is meant for is worth writing down where the program lives. Every listing on this site carries a Dialect: line in its header for that reason, and this one names the three compile commands it was written for.
Compile and run
From the site folder, with GnuCOBOL 3.x installed:
cobc -x code/gnucobol-std-dialects-ibm.cob
./gnucobol-std-dialects-ibm
cobc -x -std=ibm code/gnucobol-std-dialects-ibm.cob
./gnucobol-std-dialects-ibm
python code/gnucobol-std-dialects-ibm.py --config /usr/local/share/gnucobol/config
python code/gnucobol-std-dialects-ibm.py --config /usr/local/share/gnucobol/config --expect ibm
python code/gnucobol-std-dialects-ibm.py --selftest
The second and fourth commands should print the two listings above. The companion needs the configuration folder of a GnuCOBOL installation; cobc --info names it, and a 3.2 source tree has the same files in config. With --config alone it prints the settings table, --expect prints the probe's expected output for a dialect (add -f options after it, as you would to cobc), --words prints the reserved-word summary, and --selftest runs its nine built-in checks. Given --testsuite gnucobol-3.2/tests/testsuite.src, the self-test also compares its storage, display and truncation rules with six cases from GnuCOBOL's test suite, 15 checks in all.
Limitations
- Not compiled here. The COBOL listing was checked against the 3.2 source and test suite, not by a compiler, and its expected output was worked out by the companion. We do not know which warnings, if any, cobc 3.2 prints for it.
- GnuCOBOL's idea of IBM, not IBM's. Every IBM column on this page is what GnuCOBOL's IBM files say. We did not compare them with an IBM compiler or with IBM's documentation for this lesson, and an IBM shop's own compile options can differ from what the files assume.
- Micro Focus is in the table only. The probe's output under
-std=mfis not traced here. One reason: in the Micro Focus dialect, 3.2 reads>>SOURCE FORMAT IS FREEas X/Open free-form source rather than GnuCOBOL's own free format (cobc/pplex.l), and we did not follow that through for this listing. - Behaviour settings, not every setting. The table shows 24 of the 127 settings the dialect files define. The rest are mostly support levels for single clauses and statements, which are easiest to read in the files themselves.
- Big-endian only. All the dialects in the table store binary items big-endian. A
binary-byteorder: nativesetting on a little-endian machine changes the bytes on disk, though not the values the program prints.
The useful habit is the one the probe makes visible: when a program moves between dialects, the PICTURE clauses stay the same and the storage under them does not. A layout with COMP fields, a counter that overflows, and a field that starts without a VALUE are the three places to look first.
Index terms cobol, gnucobol, cobc, dialects, std=ibm