[PATCH V3] New version of proc whole and dependencies
Jose E. Marchesi
jemarch@gnu.org
Tue Jul 14 21:32:21 GMT 2026
Hi Chris.
This is looking good! :)
We will need some tests added to gcc/gcc/testsuite/algol68/execute, in
files like whole-1.a68, whole-2.a68, ...
For example:
whole-1.a68
if bits_width = 32
then assert(whole(max_int,0) = "4294967296");
...
elif bits_width = 64
then assert(whole(max_int,0) = "18446744073709551616");
...
fi
> Good morning,
>
> I am proposing a "squashed" version of the two patches so far
> submitted based on Jose's kind advice. In this patch I have not
> addressed the formatting guidelines last discussed (happy to do so in
> another) - I'm hoping to fold them in with any more review comments
> regarding the idea, the function of the code, whether this patch in
> general meets expectations (for example, is this correctly labelled in
> the subject line?).
>
>>From f0528f3b445b9ad578cee7f93a048366f6d6999c Mon Sep 17 00:00:00 2001
> From: Chris Hermansen <chris.hermansen@nuevaconsulting.com>
> Date: Sun, 12 Jul 2026 18:02:04 -0700
> Subject: [PATCH] New version of proc whole and dependencies
>
> New whole and dependencies
>
> Proposed replacement for whole() as presented in the RR on p.159.
>
> Several factors motivated me to propose this replacement for the code
> provided by the RR:
>
> This code works on the number from left to right, rather than right to left
> as seen in the RR. Working from left to right in this way requires either:
> - processing all 10 digits, meaning worthless effort for every leading zero
> digit, or
> - determining how many significant digits there are, which means some lookup
> code (I believe this is faster but I haven't benchmarked it at this point)
>
> By determining beforehand how many significant digits there are, we can
> allocate a working buffer of exactly the right length, which eliminates:
> - the (expensive) string concatenation approach used in the RR proc subwhole
> - the need to keep in mind a fixed-length buffer should GNU Algol 68 begin
> to support longer integers (128, 256, whatever)
> - the need to trim the fixed-length buffer once the converted integer is in
> place
>
> Finally, working left to right
> - eliminates the need to apply ABS to the number to be converted, thereby
> eliminating the need to depend on what ABS (- max_int - 1) produces
> (note the RR DOES NOT deal with this issue)
> - replaces one (expensive) integer division with one (less expensive)
> integer multiplication using a looked-up power of 10
>
> I have also reviewed van Vliet's proposed whole and subwhole and do not
> find them to share the same advantages, though they do only convert the
> integer when it is non-zero.
>
> Back in the 1980s and 1990s, I maintained a relatively large suite of inter-
> related Pascal programs. At one point I needed to develop some specific
> output routines not served by the Pascal compiler I was using at the time.
> In those dark days, computer time was expensive and code efficiency was
> mandatory; I ended up stumbling on the idea of converting left-to-right to
> eliminate one expensive integer division.
>
> This left-to-right approach can be easily extended to providing for thousands
> separators (or the thousands / 10 thousands / millions etc approach used in
> India). As well, it could be worth making whole, fixed and float respond to
> the locale.
>
> Seeing this nice article on the same kind of conversion, I was reminded of
> "the good old days":
>
> https://towardsdatascience.com/34-faster-integer-to-string-conversion-algorithm-c72453d25352
> ---
> libga68/standard.a68.in | 168 +++++++++++++++++++++++++++++++---------
> 1 file changed, 133 insertions(+), 35 deletions(-)
>
> diff --git a/libga68/standard.a68.in b/libga68/standard.a68.in
> index 5246679fa9f..1cf5726c8bb 100644
> --- a/libga68/standard.a68.in
> +++ b/libga68/standard.a68.in
> @@ -58,28 +58,103 @@ def
> {reti {,}}
> );
>
> + { The definition of mode Integer used by both the RR subwhole and
> this one. }
> +
> + mode Integer = union (
> + {iter L {long long } {long } {} {short } {short short }}
> + {L}int
> + {reti {,}}
> + );
> +
> + { The whole_powers_of_10 row is used to look up the appropriate
> power of 10 for
> + each integer division required to select the leading in the conversion
> + process, for each integer multiplication to eliminate the leading digit,
> + and in the lookup operator WHOLEDIGITS to determine the number
> of digits in
> + the integer to be converted. }
> +
> + int whole_max_entry := 1;
> + long long int whole_p10 := long long 1;
> + long long int whole_stop_after = long_long_max_int % long long 10;
> + while whole_p10 < whole_stop_after
> + do
> + whole_p10 *:= long long 10;
> + whole_max_entry +:= 1
> + od;
> + heap [1:whole_max_entry]long long int whole_powers_of_10;
> + while whole_max_entry > 0
> + do
> + whole_powers_of_10[whole_max_entry] := whole_p10;
> + whole_p10 %:= long long 10;
> + whole_max_entry -:= 1
> + od;
> +
> + { The WHOLEDIGITS operator is used to determine the number of
> decimal digits in a
> + number. We convert the operand to long long and make it negative if
> + necessary, to allow for twos-complement minimum (negative) integer. }
> +
> + op WHOLEDIGITS = (Integer number) int:
> + begin
> + long long int work =
> + case number
> + in
> + {iter L {long long } {long } {} {short } {short short }}
> + {iter K {} {LENG } {LENG LENG } {LENG LENG LENG }
> {LENG LENG LENG LENG }}
> + ({L}int x):
> + {K}(x > {L} 0 | -x | x)
> + {reti {,}}
> + esac;
> + int num_digits := 1;
> + for i from (LWB whole_powers_of_10) + 1 to UPB whole_powers_of_10
> + while work <= -whole_powers_of_10[i]
> + do
> + num_digits +:= 1
> + od;
> + num_digits
> + end { WHOLEDIGITS };
> +
> + { proc whole checks for a too-small width, returning a string of the error
> + character if so; it left-pads the result with blanks as necessary and the
> + sign as necessary; and it relies on subwhole to do the actual digit
> + conversion. The [] result returned is either exactly the number of chars
> + needed (width = 0) to hold the converted integer and negative sign if < 0
> + or width characters (width ≠ 0). }
> +
> +
> pub proc whole = (Number v, int width) string:
> - case v in
> - {iter L {short short} {short} {} {long} {long long}}
> - {iter L_ {short_short_} {short_} {} {long_} {long_long_}}
> - ({L} int x):
> - (int length := ABS width - (x < {L} 0 OR width > 0 | 1 | 0),
> - {L} int n := ABS x;
> - if width = 0
> - then {L} int m := n; length := 0;
> - while m %:= {L} 10; length +:= 1; m /= {L} 0
> - do ~ od
> - fi;
> - string s := subwhole (n, length);
> - if length = 0 OR char_in_string (errorchar, loc int, s)
> - then ABS width * errorchar
> - else (x < {L} 0 | "-" |: width > 0 | "+" | "") +=: s;
> - (width /= 0 | (ABS width - UPB s) * " " +=: s);
> - s
> - fi),
> - ({L} real x): fixed (x, width, 0)
> + case v
> + in
> + {iter L {long long } {long } {} {short } {short short }}
> + ({L}int x):
> + if int digits_required = WHOLEDIGITS x;
> + bool negative = x < {L}0;
> + int signs_required = (negative OR width > 0 | 1 | 0);
> + int chars_required = signs_required + digits_required;
> + int chars_available = (width = 0 | chars_required | ABS width);
> + chars_available < chars_required
> + then
> + chars_available * "*"
> + else
> + [1:chars_available]char buffer;
> + int spaces_required = chars_available - chars_required;
> + int buf_ch := 1;
> + while buf_ch <= spaces_required
> + do
> + buffer[buf_ch] := " ";
> + buf_ch +:= 1
> + od;
> + if signs_required > 0
> + then
> + buffer[buf_ch] := (negative | "-" | "+");
> + buf_ch +:= 1
> + fi;
> + subwhole(x, buffer, buf_ch);
> + buffer
> + fi
> {reti {,}}
> - esac;
> + out
> + fixed(v, width, 0)
> + esac { whole };
> +
>
> pub proc fixed = (Number v, int width, after) string:
> case v in
> @@ -137,22 +212,45 @@ def
> {reti {,}}
> esac;
>
> - { Returns a string of maximum length `width' containing a decimal
> - representation of the positive integer `v'. }
> -
> - proc subwhole = (Number v, int width) string:
> - case v in
> - {iter L {short short} {short} {} {long} {long long}}
> - {iter S {LENG LENG} {LENG} {} {SHORTEN} {SHORTEN SHORTEN}}
> - ({L} int x):
> - begin string s, {L} int n := x;
> - while dig_char ({S} (n MOD {L} 10)) +=: s;
> - n %:= {L} 10; n /= {L} 0
> - do ~ od;
> - (UPB s > width | width * errorchar | s)
> - end
> + { The RR proc subwhole looks like
> +
> + proc ℵ₀ subwhole = (number v, int width) string: { implementation };
> +
> + We deviate from that design below. This means that, should someone copy
> + proc putf from the RR, they must recognize that the subwhole mentioned
> + there is no longer defined here, and make adjustments.
> +
> + I considered calling this subwhole something else
> (whole_do_conv for example)
> + but that would mean anyone else calling subwhole hoping to get the new
> + version would silently get the old subwhole instead.
> +
> + This subwhole converts digit by digit from left to right. It
> relies on the
> + caller having padded out the buffer with spaces and sign as required and
> + begins filling digits starting at the value passed in buf_ch. The final
> + []char array and final value of buf_ch are returned. }
> +
> + proc subwhole = (Integer v, ref []char buffer, ref int buf_ch) void:
> + case int char_zero = ABS "0"; v
> + in
> + {iter L {long long } {long } {} {short } {short short }}
> + {iter K {LENG LENG } {LENG } {} {SHORTEN } {SHORTEN SHORTEN }}
> + {iter S {SHORTEN SHORTEN } {SHORTEN } {} {LENG } {LENG LENG }}k
> + {iter T {} {SHORTEN } {SHORTEN SHORTEN } {SHORTEN SHORTEN
> SHORTEN } {SHORTEN SHORTEN SHORTEN SHORTEN }}
> + ({L}int number_to_convert):
> + begin
> + {L}int work := number_to_convert;
> + while buf_ch <= UPB buffer
> + do
> + int digit_number = UPB buffer - buf_ch + 1;
> + {L}int p10 = {T}whole_powers_of_10[digit_number];
> + int digit = {S} (work % p10);
> + buffer[buf_ch] := REPR (ABS digit + char_zero);
> + buf_ch +:= 1;
> + work -:= {K}digit * p10
> + od
> + end
> {reti {,}}
> - esac;
> + esac { subwhole };
>
> { Returns a string of maximum length `width' containing a rounded
> decimal representation of the positive real number `v'; if
> --
> 2.53.0
More information about the Algol68
mailing list