Skip to content

Issues with numeric field variables and OFMT/CONVFMT #271

Description

@stephane-chazelas

Similar issues were reported for GNU awk in the thread at https://lists.gnu.org/archive/html/bug-gawk/2026-06/msg00001.html

Per POSIX, $0 is meant to be recomputed by joining field variables with OFS at the point any of those field variables or NF are being assigned.

And similarly, field variables are meant to be recomputed by splitting $0 based on FS at the point $0 is assigned (explicitly or via reading a new record, or any other way).

The problem here is that onetrueawk like gawk implements some performance optimisations by which that joining/splitting is only done later if necessary (or if OFS, respectively FS are modified) but when field variables (respectively $0) are numeric, those optimisations overlook the fact that it's the value of CONVFMT at the time of the field variable (resp $0) were modified that should be used to convert those numbers to string prior to joining/splitting.

In the case of onetrueawk, there also seems to be some confusion between OFMT and CONVFMT in some cases.

$ echo 5 3 | onetrueawk '{CONVFMT="%.3f"; OFMT="%03e"; $0 = $1/$2; CONVFMT="%.0f"; OFMT="%.0e"; print $1}'
2

Upon $0 = $1/$2, the string representation of $0 should have been 1.667 (as CONVFMT contained %.3f at that point), which should have been split into $1 == "1.667". Here, as an optimisation, that splitting was done at the point $1 was referenced but used the wrong value of CONVFMT, the one that was set after $0 was assigned.

$ echo 5 3 | onetrueawk '{CONVFMT="%.3f"; OFMT="%03e"; $0 = $1/$2; CONVFMT="%.0f"; OFMT="%.0e"; print $0, $1}'
2e+00 2e+00

It's worse here as it's the value of OFMT that was used, which is only meant to be used for output, while it's the value of CONVFMT from the time $0 was modified that should have been used to convert $0 to string prior to splitting to compute the field variables.

For the symmetric case of $0 computed out of field variables:

$ echo 5 3 | onetrueawk '{CONVFMT="%.3f"; OFMT="%03e"; $1 = $1/$2; CONVFMT="%.0f"; OFMT="%.0e"; print $0}'
2 3

Same thing, it used the wrong value of CONVFMT.

$ echo 5 3 | onetrueawk '{CONVFMT="%.3f"; OFMT="%03e"; $1 = $1/$2; CONVFMT="%.0f"; OFMT="%.0e"; print $1, $0}'
2e+00 2e+00 3

Same confusion with OFMT.

$ echo 5 3 | onetrueawk '{CONVFMT="%.3f"; OFMT="%03e"; $1 = $1/$2; OFS="+"; CONVFMT="%.0f"; OFMT="%.0e"; print $0}'
1.667 3

Correct in this case, but only because assigning OFS presumably triggers the joining (with the previous value of OFS), so here before CONVFMT is modified.

$ echo 5 3 | onetrueawk '{CONVFMT="%.3f"; OFMT="%03e"; $1 = $1/$2; CONVFMT="%.0f"; OFS="+"; OFMT="%.0e"; print $0}'
2 3

Back to being wrong as that's now done after CONVFMT was modified.

As also noted for gawk, this kind of issue is unlikely to be hit in practice as if CONVFMT/OFMT are being set, that's generally once and for all at the start.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions