Base conversion program

TrustyTony 0 Tallied Votes 921 Views Share

There was little buggy version of Python base conversion around, so here quick hack for my own program, including test with random numbers to verify it.

# -*- coding: cp1252 -*-
from random import randint

symbols = '0123456789abcdefghijklmnopqrstuvwxyzåäö'
symbol_values = dict(reversed(v) for v in enumerate(symbols))

def int_to_base(number, base):
    if base > len(symbols) or base < 2:
        raise ValueError('Invalid base %i' % base)
    s = ''
    if number < 0:
        number, neg = -number, True
    else:
        neg = False
        
    while number:
        number, this = divmod(number, base)
        s = symbols[this] + s 

    return (neg * '-' ) + s

def value_in_base(string, base):
    minus = string.lstrip().startswith('-')       
    v = sum(symbol_values[sym] * base ** power
               for power, sym in enumerate(reversed(string.strip(' -'))))
    return -v if minus else v

num_check = 100
for count in range(num_check):
    num, b = randint(-100000, 10000), randint(2,len(symbols))
    print('Original %i' % num)
    v = int_to_base(num, b)
    print('%s base %i' % (v, b))
    print(value_in_base(v, b))
    if b < 37:
        print('check: %s\n' % int(v, b))
        assert int(v, b) == num
TrustyTony 888 ex-Moderator Team Colleague Featured Poster

Original was not really buggy, but was missing base check for other function, so here one slightly improved, with also balanced range for testing, instead of mostly negative:

# -*- coding: cp1252 -*-
from random import randint

symbols = '0123456789abcdefghijklmnopqrstuvwxyzåäö'
symbol_values = dict(reversed(v) for v in enumerate(symbols))

def int_to_base(number, base):
    if base > len(symbols) or base < 2:
        raise ValueError('Invalid base %i' % base)
    s = ''
    if number < 0:
        number, neg = -number, True
    else:
        neg = False
        
    while number:
        number, this = divmod(number, base)
        s = symbols[this] + s 

    return str((neg * '-' ) + s)

def value_in_base(string, base):
    if base > len(symbols) or base < 2:
        raise ValueError('Invalid base %i' % base)
    minus = string.lstrip().startswith('-')       
    v = sum(symbol_values[sym] * base ** power
               for power, sym in enumerate(reversed(string.lower().strip(' -'))))
    return -v if minus else v

num_check = 100
for count in range(num_check):
    num, b = randint(-10000, 10000), randint(2,len(symbols))
    print('\nOriginal %i' % num)
    v = int_to_base(num, b)
    print('%s base %i' % (v, b))
    print(value_in_base(v, b))
    assert value_in_base(v, b) == num

What is missing is unary coding. Also if all symbols would be defined for UTF-16, big numbers could be printed in UTF-16 in base 65536, otherwise with printable set of 256 letters, numbers binary representation could be easily printed as base 256, one letter per byte.

TrustyTony 888 ex-Moderator Team Colleague Featured Poster

Of course the bug had to be there, if number is 0, the loop is not entered so line 20 must be revised to:

return str((neg * '-') + s) if s else '0'
TrustyTony 888 ex-Moderator Team Colleague Featured Poster

To be more restrictive on value of the symbols in numbers you can initialize each time the dictionary for acceptable symbols:

def value_in_base(string, base):
    if base > len(symbols) or base < 2:
        raise ValueError('Invalid base %i' % base)
    symbol_values = dict(reversed(v) for v in enumerate(symbols[:base]))
    minus = string.lstrip().startswith('-')
    v = sum(symbol_values[sym] * base ** power
               for power, sym in enumerate(reversed(string.lower().lstrip(' -'))))
    return -v if minus else v

Or you can be satisfied with simple linear search:

def value_in_base(string, base):
    if base > len(symbols) or base < 2:
        raise ValueError('Invalid base %i' % base)
    minus = string.lstrip().startswith('-')
    string = string.lower().lstrip('-')
    assert all(c in symbols[:base] for c in string)
    v = sum(symbols.find(sym) * base ** power
               for power, sym in enumerate(reversed(string)))
    return -v if minus else v
yuvalm2 0 Newbie Poster

Random testing is bad practice.

If your test fails, you may not be able to recreate the scenario which caused the bug.

It is usually best to try to build both deterministic programs and unit-tests. (Unless randomness is strictly required in your application)

TrustyTony 888 ex-Moderator Team Colleague Featured Poster

I have stated in post in related thread in C forum, that also planned corner cases are necessary, but in my opinion it is good to through in some random values to sometimes catch left out corner values. Of course that is connected with planning for the program, so for new program I think it is good to do the logic of program and test cases side by side. First test to pass would be example usage scenarios for the code. Finding bug places is usually closely rfelated to branching conditions, user input values, exceptions from IO...

Like Dijkstra said it is generally not possible to prove that bug does not exist, only that you have .

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.