After messing around a bit, this is what I came up with. I submit it to you humbly, referring to Ignacio's warning. Please let me know if you find any flaws. By the way, I have no reason to believe that the precision argument gives anything more than the vague confidence that the first precision digits are pretty close to the correct ones.
def base3int(x): x = int(x) exponents = range(int(math.log(x, 3)), -1, -1) for e in exponents: d = int(x // (3 ** e)) x -= d * (3 ** e) yield d def base3fraction(x, precision=1000): x = x - int(x) exponents = range(-1, (-precision - 1) * 2, -1) for e in exponents: d = int(x // (3 ** e)) x -= d * (3 ** e) yield d if x == 0: break
These are iterators that return ints. Let me know if you need string conversion; but I think you can handle it.
EDIT: Actually, looking at this, it seems that the line if x == 0: break after yield in base3fraction gives you almost arbitrary precision. I went ahead and added this. However, I am leaving the precision argument; it makes sense to limit this amount.
Also, if you want to convert back to decimals, this is what I used to verify above.
sum(d * (3 ** (-i - 1)) for i, d in enumerate(base3fraction(x)))
Update
For some reason, I felt inspired in this issue. Here is a much more generalized solution. This returns two generators that generate sequences of integers representing the integral and fractional parts of a given number in an arbitrary base. Note that this only returns two generators to distinguish parts of the number; the algorithm for generating numbers is the same in both cases.
def convert_base(x, base=3, precision=None): length_of_int = int(math.log(x, base)) iexps = range(length_of_int, -1, -1) if precision == None: fexps = itertools.count(-1, -1) else: fexps = range(-1, -int(precision + 1), -1) def cbgen(x, base, exponents): for e in exponents: d = int(x // (base ** e)) x -= d * (base ** e) yield d if x == 0 and e < 0: break return cbgen(int(x), base, iexps), cbgen(x - int(x), base, fexps)