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.