Search code examples
pythonmathfloating-pointnumpy

Function to determine if two numbers are nearly equal when rounded to n significant decimal digits


I have been asked to test a library provided by a 3rd party. The library is known to be accurate to n significant figures. Any less-significant errors can safely be ignored. I want to write a function to help me compare the results:

def nearlyequal(a, b, sigfig=5):

The purpose of this function is to determine if two floating-point numbers (a and b) are approximately equal. The function will return True if a==b (exact match) or if a and b have the same value when rounded to sigfig significant-figures when written in decimal.

Can anybody suggest a good implementation? I've written a mini unit-test. Unless you can see a bug in my tests then a good implementation should pass the following:

assert nearlyequal(1, 1, 5) 
assert nearlyequal(1.0, 1.0, 5) 
assert nearlyequal(1.0, 1.0, 5) 
assert nearlyequal(-1e-9, 1e-9, 5) 
assert nearlyequal(1e9, 1e9 + 1 , 5) 
assert not nearlyequal(1e4, 1e4 + 1, 5) 
assert nearlyequal(0.0, 1e-15, 5) 
assert not nearlyequal(0.0, 1e-4, 6) 

Additional notes:

  1. Values a and b might be of type int, float or numpy.float64. Values a and b will always be of the same type. It's vital that conversion does not introduce additional error into the function.
  2. Lets keep this numerical, so functions that convert to strings or use non-mathematical tricks are not ideal. This program will be audited by somebody who is a mathematician who will want to be able to prove that the function does what it is supposed to do.
  3. Speed... I've got to compare a lot of numbers so the faster the better.
  4. I've got numpy, scipy and the standard-library. Anything else will be hard for me to get, especially for such a small part of the project.

Solution

  • There is a function assert_approx_equal in numpy.testing (source here) which may be a good starting point.

    def assert_approx_equal(actual, desired, significant=7, err_msg='', verbose=True):
        """
        Raise an assertion if two items are not equal up to significant digits.
    
        .. note:: It is recommended to use one of `assert_allclose`,
                  `assert_array_almost_equal_nulp` or `assert_array_max_ulp`
                  instead of this function for more consistent floating point
                  comparisons.
    
        Given two numbers, check that they are approximately equal.
        Approximately equal is defined as the number of significant digits
        that agree.