write/1 prints whole-valued floats without a decimal point (non-ISO output) #32

Closed
opened 2026-06-23 17:22:41 +00:00 by navicore · 1 comment
Owner

Summary

write/1 renders a float with no fractional part as if it were an integer —
dropping the .0. The value's type is internally correct (it really is a
float), but the textual output is indistinguishable from the integer and would
not read back as a float, which violates ISO write semantics for floats.

Version

plgc 0.3.0.

Reproduction

Given any program file p.pl (e.g. p.):

$ plgc run p.pl --query "X is 2.0, write(X), nl" --format text
2
X = 2

$ plgc run p.pl --query "X is 2 ** 10, write(X), nl" --format text
1024
X = 1024

$ plgc run p.pl --query "X is 7 / 2, write(X), nl" --format text
3.5
X = 3.5

(The first line of each run is write/1's own output, followed by nl; the
X = … line is the binding display. Both show the same dropped .0, confirming
this is write/1 itself and not only the CLI result formatter.)

Expected (ISO)

A float must be written with a decimal point and at least one fractional digit,
so write(2.0)2.0 and write(1024.0)1024.0. The written form must read
back as a float (round-trip).

Actual

write(2.0) emits 2; write(1024.0) emits 1024. Non-whole floats are
correct (write(3.5)3.5), so the defect is specific to whole-valued floats.

Value semantics are otherwise correct

?- X is 2 ** 10, float(X).     % true  (it really is a float)
?- X is 2 ** 10, integer(X).   % false
?- 1024.0 == 1024.             % false (distinct terms)
?- 1024.0 =:= 1024.            % true

So this is purely an output-formatting compliance bug sitting on top of
correct value/type semantics.

Found while authoring the loglings
curriculum.

## Summary `write/1` renders a float with no fractional part as if it were an integer — dropping the `.0`. The value's *type* is internally correct (it really is a float), but the textual output is indistinguishable from the integer and would not read back as a float, which violates ISO write semantics for floats. ## Version `plgc 0.3.0`. ## Reproduction Given any program file `p.pl` (e.g. `p.`): ``` $ plgc run p.pl --query "X is 2.0, write(X), nl" --format text 2 X = 2 $ plgc run p.pl --query "X is 2 ** 10, write(X), nl" --format text 1024 X = 1024 $ plgc run p.pl --query "X is 7 / 2, write(X), nl" --format text 3.5 X = 3.5 ``` (The first line of each run is `write/1`'s own output, followed by `nl`; the `X = …` line is the binding display. Both show the same dropped `.0`, confirming this is `write/1` itself and not only the CLI result formatter.) ## Expected (ISO) A float must be written with a decimal point and at least one fractional digit, so `write(2.0)` → `2.0` and `write(1024.0)` → `1024.0`. The written form must read back as a float (round-trip). ## Actual `write(2.0)` emits `2`; `write(1024.0)` emits `1024`. **Non-whole** floats are correct (`write(3.5)` → `3.5`), so the defect is specific to whole-valued floats. ## Value semantics are otherwise correct ``` ?- X is 2 ** 10, float(X). % true (it really is a float) ?- X is 2 ** 10, integer(X). % false ?- 1024.0 == 1024. % false (distinct terms) ?- 1024.0 =:= 1024. % true ``` So this is purely an **output-formatting** compliance bug sitting on top of correct value/type semantics. Found while authoring the [loglings](https://git.navicore.tech/navicore/loglings) curriculum.
Author
Owner

#34

https://git.navicore.tech/navicore/patch-prolog/pulls/34
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
navicore/patch-prolog#32
No description provided.