Skip to content

Commit d9a7305

Browse files
committed
FIXED: formatting of floats in library(format)
This addresses mthom#2771, using a new internal predicate resorting to to_string(). Many thanks to Trevor Merrifield for reporting this issue! There is still room for improvements: With better support for inspecting floats, more of this logic can be moved to Prolog; also, there may be a way to obtain the float with greater precision, and with fewer needed Rust primitives.
1 parent 00e6e32 commit d9a7305

File tree

4 files changed

+65
-31
lines changed

4 files changed

+65
-31
lines changed

build/instructions_template.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,8 @@ enum SystemClauseType {
343343
NoSuchPredicate,
344344
#[strum_discriminants(strum(props(Arity = "2", Name = "$number_to_chars")))]
345345
NumberToChars,
346+
#[strum_discriminants(strum(props(Arity = "2", Name = "$float_to_chars")))]
347+
FloatToChars,
346348
#[strum_discriminants(strum(props(Arity = "2", Name = "$number_to_codes")))]
347349
NumberToCodes,
348350
#[strum_discriminants(strum(props(Arity = "3", Name = "$op")))]
@@ -1769,6 +1771,7 @@ fn generate_instruction_preface() -> TokenStream {
17691771
&Instruction::CallNextEP |
17701772
&Instruction::CallNoSuchPredicate |
17711773
&Instruction::CallNumberToChars |
1774+
&Instruction::CallFloatToChars |
17721775
&Instruction::CallNumberToCodes |
17731776
&Instruction::CallOpDeclaration |
17741777
&Instruction::CallOpen |
@@ -2007,6 +2010,7 @@ fn generate_instruction_preface() -> TokenStream {
20072010
&Instruction::ExecuteNextEP |
20082011
&Instruction::ExecuteNoSuchPredicate |
20092012
&Instruction::ExecuteNumberToChars |
2013+
&Instruction::ExecuteFloatToChars |
20102014
&Instruction::ExecuteNumberToCodes |
20112015
&Instruction::ExecuteOpDeclaration |
20122016
&Instruction::ExecuteOpen |

src/lib/format.pl

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2-
Written 2020-2024 by Markus Triska ([email protected])
2+
Written 2020-2025 by Markus Triska ([email protected])
33
Part of Scryer Prolog.
44
I place this code in the public domain. Use it in any way you want.
55
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
@@ -284,36 +284,12 @@
284284
cells(Fs, Args, 0, [], VNs).
285285
cells([~,s|Fs], [Arg|Args], Tab, Es, VNs) --> !,
286286
cells(Fs, Args, Tab, [chars(Arg)|Es], VNs).
287-
cells([~,f|Fs], [Arg|Args], Tab, Es, VNs) --> !,
288-
{ G = format_number_chars(Arg, Chars) },
289-
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
287+
cells([~,f|Fs], Args, Tab, Es, VNs) --> !,
288+
cells([~,'6',f|Fs], Args, Tab, Es, VNs).
290289
cells([~|Fs0], Args0, Tab, Es, VNs) -->
291290
{ numeric_argument(Fs0, Num, [f|Fs], Args0, [Arg|Args]) },
292291
!,
293-
{ G = (format_number_chars(Arg, Cs0),
294-
phrase(upto_what(Bs, .), Cs0, Cs),
295-
( Num =:= 0 -> Chars = Bs
296-
; ( Cs = ['.'|Rest] ->
297-
length(Rest, L),
298-
( Num < L ->
299-
length(Ds, Num),
300-
append(Ds, _, Rest)
301-
; Num =:= L ->
302-
Ds = Rest
303-
; Num > L,
304-
Delta is Num - L,
305-
% we should look into the float with
306-
% greater accuracy here, and use the
307-
% actual digits instead of 0.
308-
length(Zs, Delta),
309-
maplist(=('0'), Zs),
310-
append(Rest, Zs, Ds)
311-
)
312-
; length(Ds, Num),
313-
maplist(=('0'), Ds)
314-
),
315-
append(Bs, ['.'|Ds], Chars)
316-
)) },
292+
{ G = float_digits_chars(Arg, Num, Chars) },
317293
cells(Fs, Args, Tab, [chars(Chars),goal(G)|Es], VNs).
318294
cells([~,r|Fs], Args, Tab, Es, VNs) --> !,
319295
cells([~,'8',r|Fs], Args, Tab, Es, VNs).
@@ -368,9 +344,30 @@
368344
Fs1 = [_|_] },
369345
cells(Fs, Args, Tab, [chars(Fs1)|Es], VNs).
370346

371-
format_number_chars(N0, Chars) :-
372-
N is N0, % evaluate compound expression
373-
number_chars(N, Chars).
347+
348+
float_digits_chars(F0, Num, Chars) :-
349+
F is float(F0), % evaluate compound expression and convert to float
350+
'$float_to_chars'(F, Cs0),
351+
phrase(upto_what(Bs, .), Cs0, Cs),
352+
( Num =:= 0 -> Chars = Bs
353+
; ( Cs = ['.'|Rest] ->
354+
length(Rest, L),
355+
( Num < L ->
356+
length(Ds, Num),
357+
append(Ds, _, Rest)
358+
; Num =:= L ->
359+
Ds = Rest
360+
; Num > L,
361+
Delta is Num - L,
362+
length(Zs, Delta),
363+
maplist(=('0'), Zs),
364+
append(Rest, Zs, Ds)
365+
)
366+
; length(Ds, Num),
367+
maplist(=('0'), Ds)
368+
),
369+
append(Bs, ['.'|Ds], Chars)
370+
).
374371

375372
n_newlines(N0) --> { N0 > 0, N is N0 - 1 }, [newline], n_newlines(N).
376373
n_newlines(0) --> [].
@@ -564,6 +561,15 @@
564561
565562
?- format("~12r", [300]).
566563
210 true.
564+
565+
?- format("~f", [3]).
566+
3.000000 true.
567+
568+
?- format("~6f", [1.0e20]).
569+
100000000000000000000.000000 true.
570+
571+
?- format("~50f", [3.0e-20]).
572+
0.00000000000000000003000000000000000000000000000000 true.
567573
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
568574

569575
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

src/machine/dispatch.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3750,6 +3750,14 @@ impl Machine {
37503750
self.number_to_chars();
37513751
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
37523752
}
3753+
&Instruction::CallFloatToChars => {
3754+
self.float_to_chars();
3755+
step_or_fail!(self, self.machine_st.p += 1);
3756+
}
3757+
&Instruction::ExecuteFloatToChars => {
3758+
self.float_to_chars();
3759+
step_or_fail!(self, self.machine_st.p = self.machine_st.cp);
3760+
}
37533761
&Instruction::CallNumberToCodes => {
37543762
self.number_to_codes();
37553763
step_or_fail!(self, self.machine_st.p += 1);

src/machine/system_calls.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2834,6 +2834,22 @@ impl Machine {
28342834
self.machine_st.unify_complete_string(chars_atom, chs);
28352835
}
28362836

2837+
#[inline(always)]
2838+
pub(crate) fn float_to_chars(&mut self) {
2839+
let n = self.deref_register(1);
2840+
let chs = self.deref_register(2);
2841+
2842+
let string = match Number::try_from(n) {
2843+
Ok(Number::Float(OrderedFloat(n))) => n.to_string(),
2844+
_ => {
2845+
unreachable!()
2846+
}
2847+
};
2848+
2849+
let chars_atom = AtomTable::build_with(&self.machine_st.atom_tbl, string.trim());
2850+
self.machine_st.unify_complete_string(chars_atom, chs);
2851+
}
2852+
28372853
#[inline(always)]
28382854
pub(crate) fn number_to_codes(&mut self) {
28392855
let n = self.deref_register(1);

0 commit comments

Comments
 (0)