correct handling of floats with N digits (~f and ~Nf)

This addresses #2771. Many thanks to @tmerr for reporting the issue!

The code was posted by @UWN in:

    https://github.com/mthom/scryer-prolog/discussions/2805

Many thanks! With additional inputs by @adri326 and @notoria, who also
posted interesting approaches which could help to increase precision
in cases that are currently not ideally handled. Thank you all!

Please see the issue and discussion for more information.
This commit is contained in:
Markus Triska
2025-02-28 08:14:34 +01:00
parent a0e3283d64
commit 2d4fcf9714

View File

@@ -285,36 +285,12 @@ cells([~|Fs0], Args0, Tab, Es, VNs) -->
cells(Fs, Args, 0, [], VNs).
cells([~,s|Fs], [Arg|Args], Tab, Es, VNs) --> !,
cells(Fs, Args, Tab, [chars(Arg)|Es], VNs).
cells([~,f|Fs], [Arg|Args], Tab, Es, VNs) --> !,
{ G = format_number_chars(Arg, Chars) },
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
cells([~,f|Fs], Args, Tab, Es, VNs) --> !,
cells([~,'6',f|Fs], Args, Tab, Es, VNs).
cells([~|Fs0], Args0, Tab, Es, VNs) -->
{ numeric_argument(Fs0, Num, [f|Fs], Args0, [Arg|Args]) },
!,
{ G = (format_number_chars(Arg, Cs0),
phrase(upto_what(Bs, .), Cs0, Cs),
( Num =:= 0 -> Chars = Bs
; ( Cs = ['.'|Rest] ->
length(Rest, L),
( Num < L ->
length(Ds, Num),
append(Ds, _, Rest)
; Num =:= L ->
Ds = Rest
; Num > L,
Delta is Num - L,
% we should look into the float with
% greater accuracy here, and use the
% actual digits instead of 0.
length(Zs, Delta),
maplist(=('0'), Zs),
append(Rest, Zs, Ds)
)
; length(Ds, Num),
maplist(=('0'), Ds)
),
append(Bs, ['.'|Ds], Chars)
)) },
{ G = phrase(float_with_n_decimal_digits(Arg, Num), Chars) },
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
cells([~,r|Fs], Args, Tab, Es, VNs) --> !,
cells([~,'8',r|Fs], Args, Tab, Es, VNs).
@@ -369,9 +345,18 @@ cells(Fs0, Args, Tab, Es, VNs) -->
Fs1 = [_|_] },
cells(Fs, Args, Tab, [chars(Fs1)|Es], VNs).
format_number_chars(N0, Chars) :-
N is N0, % evaluate compound expression
number_chars(N, Chars).
float_with_n_decimal_digits(F, N) -->
{ Fr is abs(float_fractional_part(F)),
FrR0 is round(Fr*10^N),
I0 is truncate(F),
( FrR0 >= 10^N
-> I is I0+truncate(sign(F)), FrR = FrR0
; I = I0, FrR is FrR0+10^N
)
},
( { I=0, F<0, FrR>10^N } -> "-0" ; { number_chars(I, Is) }, seq(Is) ),
".",
( { FrR = 1 } -> "0" ; { number_chars(FrR, ['1'|FrRs]) }, seq(FrRs) ).
n_newlines(N0) --> { N0 > 0, N is N0 - 1 }, [newline], n_newlines(N).
n_newlines(0) --> [].