Compose Bijective Base 10 string for an integer
I found a fairly fast way to convert strings representing Base 10 numbers to strings that represent Bijective Base 10 numbers. That’s a string-to-string conversion, no integer type is involved. What happens if you have an integer type variable and you want a Bijective Base 10 string representing it?
I found a method to compose a Bijective Base 10 string from an integer type value.
1 func Btoa(n uint64) string {
2 if n == 0 {
3 return "0"
4 }
5 var places [20]rune
6 place := 19
7 for n != 0 {
8 value := rune(n % 10)
9 if value == 0 {
10 value = 'A' - '0'
11 n -= 10
12 }
13 places[place] = value + '0'
14 n /= 10
15 place--
16 }
17 return string(places[place+1:])
18 }
Lines 2-4 ruin the whole “there is no zero” vibe, but I didn’t want to clutter code with error handling.
I determined the size of array places by
counting digits in the decimal representation of
264-1,
18446744073709551615.
This algorithm works by repeatedly
finding the value of the 1s-place
(line 8),
then dividing by 10 (line 14).
Because the value is a Go uint64,
Divide-by-10 doesn’t create a fractional value,
it chops off the 1s-place.
Sooner or later, after no more than 20 divisions-by-10,
the value becomes 0 (zero), and the loop terminates.
The only thing different
from converting an integer type value to ordinary
Base 10 is lines 9-12.
If the 1s-place has a value of zero,
the algorithm “borrows” 10 from the value
before dividing by 10.
The array places keeps
the Unicode code point of the printable
character that matches the place-value of
a Base 10 string representation at each index.
Once the variable that contained the
value-to-be-converted attains a zero value,
the places array, full of Unicode code points,
gets converted to a Go string type.
There are two amusing aspects of this algorithm.
First, the find 1s-place value with a
modulo 10 operation, then divide by 10
method
is exactly how the Go fmt package performs
the string conversion for %d formatting verbs.
fmt package code actually checks errors,
and allocates memory more carefully,
but the core method is the same.
How about that?
Second, borrowing a ten, lines 9-12,
reminds me of the old grade school
“long subtraction” algorithm.
Situations exist in long subtraction
where you end up borrowing a 10,
and then borrowing a 10 from the place two over:
256 - 177 for example.
You borrow a 10 from the 50
so as to subtract 7 from 16.
Then you borrow a 10 from 200
so as to subtract a 7 from 14.
My algorithm conceptually never borrows more than a single 10 from the next (base 10) place to the left. Because the algorithm works with a value in a variable, and not a set of places as long subtraction does, there’s always a 10 to “borrow from the place value to the left”. There’s no need to check if the remains of the number are non-zero, either. The for-loop exits when the remains have a zero value, so the subtraction at line 11 never creates negative remains.