Now you can spare a further mpq_canonicalize(). Namely here,
I guess rational/1 will call mpq_canonicalize(), right?
rat_rationalize3(Flt, Rat, List) :-
R is rational(Flt), rational(R, N, D),
rat_iter3((N,D), (1,0), (0,1), Flt, Rat, List).
Replace the combo rational/1 and rational/3 by something
else, that doesn’t call mpq_canonicalize(). The continued fraction
result will be the same, with and without mpq_canonicalize()
in this part of the code. Omitting the mpq_canonicalize() in this
part of the code, is doing the underlying GCD algorithm, which
is part of rationalize/1 divmod/4 loop anyway only once, and
not twice. If we already do mpq_canonicalize() we will do
a full GDC algorithm just up front. And then the rationalize/1
divmod/4 loop will do an GCD algorithm again, although not
necessarily a full one, because of the stopping condition.
Thats quite some unnessary redundancy, the up front full GCD
algorthm. Although it might help reduce the size of the arguments,
I guess the rationalize/1 divmod/4 loop will anyway do the same,
reduce the size of the arguments, only it will do it on the fly a little
later. See also how rationalize/1 does indeed some GCD algorithm:
Continued Fractions on the Stern-Brocot Tree
https://www.cut-the-knot.org/blue/ContinuedFractions.shtml
Edit 03.01.2023
On my system I see indeed a small gain, around 10% speed-up:
/* Jekejeke Prolog 1.5.5, Windows, JDK 19 */
/* With mpq_canonicalize(), i.e. the combo rational/1 and rational/3 */
?- X is pi, time((between(1,10000,_), Y is rationalize(X), fail; true)).
% Threads 610 ms, GC 4 ms, Up 631 ms (Current 01/03/23 12:15:47)
X = 3.141592653589793.
/* Without mpq_canonicalize(), i.e. something else */
?- X is pi, time((between(1,10000,_), Y is rationalize(X), fail; true)).
% Threads 563 ms, GC 5 ms, Up 569 ms (Current 01/03/23 12:29:34)
X = 3.141592653589793.
The something else is this here:
rat_start(V#W, V, W) :- !.
rat_start(F, V, W) :-
sys_float_mantissa(F, M),
sys_float_exponent(F, E),
sys_float_radix(F, B),
(E < 0 ->
V = M,
user: -(E, H),
user: ^(B, H, W);
user: ^(B, E, H),
user: *(M, H, V),
W = 1).
It will deliver this nominator and denominator, already for the machine
floating point value 0.1 then omitting mpq_canonicalize() gives a slightly
different starting pair, scaled by a common factor 2 in this example:
?- R is rational(0.1), rational(R, V, W).
V = 3602879701896397, W = 36028797018963968.
?- rat_start(0.1, V, W).
V = 7205759403792794, W = 72057594037927936.
But there is no harm, rationalize/1 does still the same thing, because the
D in divmod/4 of the new rationalize/1 algorithm will be the same, even
if the arguments are both non-canonical, i.e. scaled by some common factor:
?- X is rationalize(0.1).
X = 1#10.