Approximately Equal « Python Recipes « ActiveState Code

  • Skip to Search
  • Skip to Navigation
  • Skip to Content
  • Community
  • |
  • Code
  • |
  • Docs
  • |
  • Downloads ▼
  • |
  • more ▼
Welcome, guest | Sign In | My Account | Store | Cart ActiveState Code » Recipes Languages Tags Authors Sets Approximately Equal (Python recipe) by Steven D'Aprano ActiveState Code (http://code.activestate.com/recipes/577124/)

Generic "approximately equal" function for any object type, with customisable error tolerance.

When called with float arguments, approx_equal(x, y[, tol[, rel]) compares x and y numerically, and returns True if y is within either absolute error tol or relative error rel of x, otherwise return False. The function defaults to sensible default values for tol and rel.

For any other pair of objects, approx_equal() looks for a method __approx_equal__ and, if found, calls it with arbitrary optional arguments. This allows types to define their own concept of "close enough".

Python, 56 lines Download Copy to clipboard
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 51 52 53 54 55 56def _float_approx_equal(x, y, tol=1e-18, rel=1e-7): if tol is rel is None: raise TypeError('cannot specify both absolute and relative errors are None') tests = [] if tol is not None: tests.append(tol) if rel is not None: tests.append(rel*abs(x)) assert tests return abs(x - y) <= max(tests) def approx_equal(x, y, *args, **kwargs): """approx_equal(float1, float2[, tol=1e-18, rel=1e-7]) -> True|False approx_equal(obj1, obj2[, *args, **kwargs]) -> True|False Return True if x and y are approximately equal, otherwise False. If x and y are floats, return True if y is within either absolute error tol or relative error rel of x. You can disable either the absolute or relative check by passing None as tol or rel (but not both). For any other objects, x and y are checked in that order for a method __approx_equal__, and the result of that is returned as a bool. Any optional arguments are passed to the __approx_equal__ method. __approx_equal__ can return NotImplemented to signal that it doesn't know how to perform that specific comparison, in which case the other object is checked instead. If neither object have the method, or both defer by returning NotImplemented, approx_equal falls back on the same numeric comparison used for floats. >>> almost_equal(1.2345678, 1.2345677) True >>> almost_equal(1.234, 1.235) False """ if not (type(x) is type(y) is float): # Skip checking for __approx_equal__ in the common case of two floats. methodname = '__approx_equal__' # Allow the objects to specify what they consider "approximately equal", # giving precedence to x. If either object has the appropriate method, we # pass on any optional arguments untouched. for a,b in ((x, y), (y, x)): try: method = getattr(a, methodname) except AttributeError: continue else: result = method(b, *args, **kwargs) if result is NotImplemented: continue return bool(result) # If we get here without returning, then neither x nor y knows how to do an # approximate equal comparison (or are both floats). Fall back to a numeric # comparison. return _float_approx_equal(x, y, *args, **kwargs)

It is very common to need to test whether two floats are nearly equal, or equal to within some rounding error. This function provides a flexible way to make such tests, using either an absolute tolerance or a relative error.

Other types may also define their own version of "close enough":

>>> class MyStr(str): ... def __approx_equal__(self, other, delta=2): ... count = abs(len(self) - len(other)) ... for c,k in zip(self, other): ... count += c != k ... return count <= delta ... >>> s = MyStr('abcdef') >>> approx_equal('aXYZef', s) False >>> approx_equal('aXYZef', s, delta=3) True Tags: comparisons, distance, math, maths, numeric, string

1 comment

someonesdad 13 years, 6 months ago # | flag

Nice function, Steven. I've been doing numerical computing with python for about 12 years and I always write something like this when I need it. Your implementation is better -- so now it's going in my utility module. Thanks!

Created by Steven D'Aprano on Wed, 17 Mar 2010 (MIT)
Python recipes (4591)
Steven D'Aprano's recipes (22)

Tags

  • comparisons
  • distance
  • math
  • maths
  • numeric
  • string
▶ Show machine tags (4)
  • meta:language=python
  • meta:license=mit
  • meta:loc=56
  • meta:score=2

Required Modules

  • (none specified)

Other Information and Tasks

  • Licensed under the MIT License
  • Viewed 20766 times
  • Revision 1

Accounts

  • Create Account (Free!)
  • Sign In

Code Recipes

  • Recipes
  • Languages
  • Tags
  • Authors
  • Sets

Feedback & Information

  • About
  • FAQ
  • Terms of Service

ActiveState

  • ActiveState Blog
  • Perl Solutions
  • Python Solutions
  • Tcl Solutions
  • Download ActivePerl
  • Download ActivePython
  • Download ActiveTcl
  • About ActiveState
  • Careers

Privacy Policy | Contact Us | Support

© 2024 ActiveState Software Inc. All rights reserved. ActiveState®, Komodo®, ActiveState Perl Dev Kit®, ActiveState Tcl Dev Kit®, ActivePerl®, ActivePython®, and ActiveTcl® are registered trademarks of ActiveState. All other marks are property of their respective owners.

x Undo

Tag » Approx Equal Python