prec = 8 # number of decimal digits (must be under 15) class F: def __init__(self, value, full=None): self.value = float('%.*e' % (prec-1, value)) if full is None: full = self.value self.full = full def __str__(self): return str(self.value) def __repr__(self): return "F(%s, %r)" % (self, self.full) def error(self): ulp = float('1'+('%.4e' % self.value)[-5:]) * 10 ** (1-prec) return int(abs(self.value - self.full) / ulp) def __coerce__(self, other): if not isinstance(other, F): return (self, F(other)) return (self, other) def __add__(self, other): return F(self.value + other.value, self.full + other.full) def __sub__(self, other): return F(self.value - other.value, self.full - other.full) def __mul__(self, other): return F(self.value * other.value, self.full * other.full) def __div__(self, other): return F(self.value / other.value, self.full / other.full) def __neg__(self): return F(-self.value, -self.full) def __abs__(self): return F(abs(self.value), abs(self.full)) def __pow__(self, other): return F(pow(self.value, other.value), pow(self.full, other.full)) def __cmp__(self, other): return cmp(self.value, other.value) # Example: Show failure of the associative law (Knuth Vol. 2 p.214) u, v, w = F(11111113), F(-11111111), F(7.51111111) assert (u+v)+w == 9.5111111 assert u+(v+w) == 10 # Example: Show failure of the commutative law for addition assert u+v+w != v+w+u # Example: Show failure of the distributive law (Knuth Vol. 2 p.215) u, v, w = F(20000), F(-6), F(6.0000003) assert u*v == -120000 assert u*w == 120000.01 assert v+w == .0000003 assert (u*v) + (u*w) == .01 assert u * (v+w) == .006 # Example: Compare numerical accuracy of three appoaches to computing averages def avgsum(data): # Sum all of the elements return sum(data, F(0)) / len(data) def avgrun(data): # Make small adjustments to a running mean m = data[0] k = 1 for x in data[1:]: k += 1 m += (x-m)/k # Recurrence formula for mean return m def avgrun_kahan(data): # Adjustment method with Kahan error correction term m = data[0] k = 1 dm = 0 for x in data[1:]: k += 1 adjm = (x-m)/k - dm newm = m + adjm dm = (newm - m) - adjm m = newm return m import random prec = 5 data = [F(random.random()*10-5) for i in xrange(1000)] print '%s\t%s\t%s' %('Computed', 'ULP Error', 'Method') print '%s\t%s\t%s' %('--------', '---------', '------') for f in avgsum, avgrun, avgrun_kahan: result = f(data) print '%s\t%6d\t\t%s' % (result, result.error(), f.__name__) print '\n%r\tbaseline average using full precision' % result.full # Sample output from the above example # # Computed ULP Error Method # -------- --------- ------ # -0.020086 15 avgsum # -0.020061 9 avgrun # -0.020072 1 avgrun_kahan # # -0.020070327734999997 baseline average using full precision