Welcome, guest | Sign In | My Account | Store | Cart

The function <code>num_in_base</code> can be used to print a number using an arbitrary base. It allows numbers to be padded to a minimum field width, and can display negative numbers in a complemented format instead of with a leading negative sign.

The digits used can be overriden with an arbitrary sequence.

Python, 50 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def num_in_base(val, base, min_digits=1, complement=False,
                digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
    """Convert number to string in specified base
    
       If minimum number of digits is specified, pads result to at least
       that length.
       If complement is True, prints negative numbers in complement
       format based on the specified number of digits.
       Non-standard digits can be used. This can also allow bases greater
       than 36.
    """
    if base < 2: raise ValueError("Minimum base is 2")
    if base > len(digits): raise ValueError("Not enough digits for base")
    # Deal with negative numbers
    negative = val < 0
    val = abs(val)
    if complement:
        sign = ""
        max = base**min_digits
        if (val > max) or (not negative and val == max):
            raise ValueError("Value out of range for complemented format")
        if negative:
            val = (max - val)
    else:
        sign = "-" * negative
    # Calculate digits
    val_digits = []
    while val:
        val, digit = divmod(val, base)
        val_digits.append(digits[digit])
    result = "".join(reversed(val_digits))
    leading_digits = (digits[0] * (min_digits - len(result)))
    return sign + leading_digits + result

if __name__ == "__main__":
    # Quick sanity check
    for base in range(2, 37):
        for val in range(-1000, 1000):
            assert val == int(num_in_base(val, base), base)

    # Quick sanity check of complemented format
    def comp(val, base, digits):
        return num_in_base(val, base, digits, complement = True)
    for base in range(2, 37):
        for digits in range(1, 11):
            limit = base ** digits
            for val in range(-min(limit, 1000), 0):
                assert limit + val == int(comp(val, base, digits), base)
            for val in range(0, min(limit, 1000)):
                assert val == int(comp(val, base, digits), base)

I've never actually used this code in a real application - for printing in decimal or hexadecimal, the standard string formatting operations generally suffice, and decimal and hexadecimal cover the needs of most applications.

The above function is the result of a c.l.p discussion that covered how to do this in the general case. It's most useful advantage over the standard string formatting is the availability of the complemented format, which allows easy display of the actual byte value of negative numbers: <pre>Py> "%2X" % -0x55 '-55' Py> num_in_base(-0x55, 16, 2, complement=True) 'AB'</pre> The 'digits' argument adds surprising flexibility: <pre>Py> digits = 'zero one two three four five six seven eight nine'.upper().split() Py> digits = [d + " " for d in digits] Py> num_in_base(100, 10, digits=digits) 'ONE ZERO ZERO '</pre> One can easily imagine a sequence of wave file references used as the argument.

Created by Nick Coghlan on Wed, 2 Feb 2005 (PSF)
Python recipes (4591)
Nick Coghlan's recipes (11)
Python Cookbook Edition 2 (117)

Required Modules

  • (none specified)

Other Information and Tasks