123.3 + 0.1 is 123.3999999999 ?
Steven Taschuk
staschuk at telusplanet.net
Tue Jun 3 13:49:27 EDT 2003
Quoth A. Lloyd Flanagan:
[...]
> def get_one():
> yield '0'
> yield '.'
> while 1:
> yield '9'
>
> Exercise for the reader: write a program to prove that get_one()
> equals one.
Here's a hack for this purpose:
import copy
def generatorstate(genit):
# broken, really; assumes no use of globals, for example
frame = genit.gi_frame
return copy.deepcopy((frame.f_locals, frame.f_lasti))
def period(genit):
items = []
states = [generatorstate(genit)]
while True:
items.append(genit.next())
states.append(generatorstate(genit))
if states[-1] in states[:-1]:
break
i = states.index(states[-1])
return items[:i], items[i:]
def rational(genit):
integerpart = int(''.join(list(iter(genit.next, '.'))))
preamble, repeated = period(genit)
preamble = ''.join(preamble)
repeated = ''.join(repeated)
repeated = Rational(int(repeated), 10**len(repeated)-1) \
/ 10**len(preamble)
preamble = Rational(int(preamble), 10**len(preamble))
return integerpart + preamble + repeated
Then, with a suitable Rational class (see below), we have
>>> rational(get_one())
Rational(1, 1)
as desired.
A simple implementation of a Rational class:
def gcd(a, b):
while b:
a, b = b, a % b
return abs(a)
class Rational(object):
def __init__(self, numerator, denominator=1):
if denominator == 0:
raise ZeroDivisionError('%r/%r' % (numerator, denominator))
if denominator < 0:
numerator = -numerator
denominator = -denominator
g = gcd(numerator, denominator)
self.numerator = numerator//g
self.denominator = denominator//g
def __str__(self):
return '%s/%s' % (self.numerator, self.denominator)
def __repr__(self):
return 'Rational(%r, %r)' % (self.numerator, self.denominator)
def __add__(self, other):
if isinstance(other, int) or isinstance(other, long):
return self + Rational(other)
elif isinstance(other, Rational):
return Rational(self.numerator*other.denominator
+ other.numerator*self.denominator,
self.denominator*other.denominator)
else:
return NotImplemented
__radd__ = __add__
def __mul__(self, other):
if isinstance(other, int) or isinstance(other, long):
return self * Rational(other)
elif isinstance(other, Rational):
return Rational(self.numerator * other.numerator,
self.denominator * other.denominator)
else:
return NotImplemented
__rmul__ = __mul__
def invert(self):
return Rational(self.denominator, self.numerator)
def __truediv__(self, other):
if isinstance(other, int) or isinstance(other, long):
return self / Rational(other)
elif isinstance(other, Rational):
return self * other.invert()
else:
return NotImplemented
__div__ = __truediv__
def __rtruediv__(self, other):
if isinstance(other, int) or isinstance(other, long):
return Rational(other) / self
else:
return NotImplemented
__rdiv__ = __rtruediv__
--
Steven Taschuk staschuk at telusplanet.net
"I tried to be pleasant and accommodating, but my head
began to hurt from his banality." -- _Seven_ (1996)
More information about the Python-list
mailing list