Подтвердить что ты не робот

Функция для определения того, являются ли два числа почти равными при округлении до n значащих десятичных цифр

Меня попросили проверить библиотеку, предоставленную третьей стороной. Известно, что библиотека является точной и значимой. Любые менее значимые ошибки можно смело игнорировать. Я хочу написать функцию, которая поможет мне сравнить результаты:

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

Цель этой функции - определить, являются ли два числа с плавающей запятой (a и b) приблизительно равными. Функция вернет True, если a == b (точное совпадение), или если a и b имеют одинаковое значение при округлении до sigfig значимых цифр при записи в десятичном формате.

Может ли кто-нибудь предложить хорошую реализацию? Я написал мини-блок-тест. Если вы не увидите ошибку в моих тестах, тогда хорошая реализация должна передать следующее:

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 ) 

Дополнительные примечания:

  • Значения a и b могут иметь тип int, float или numpy.float64. Значения a и b всегда будут одного типа. Очень важно, чтобы преобразование не вводило дополнительную ошибку в функцию.
  • Позволяет сохранить это числовое значение, поэтому функции, которые преобразуются в строки или используют не математические трюки, не идеальны. Эта программа будет проверена кем-то, кто математик, который захочет доказать, что функция выполняет то, что она должна делать.
  • Скорость... Мне нужно сравнить множество чисел, чтобы чем быстрее, тем лучше.
  • У меня есть numpy, scipy и стандартная библиотека. Что-нибудь еще будет трудно для меня, особенно для такой небольшой части проекта.
4b9b3361

Ответ 1

В numpy.testing есть функция assert_approx_equal (источник , который может быть хорошей отправной точкой.

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.

Ответ 2

Как и у Python 3.5, стандартный способ сделать это (используя стандартную библиотеку) с помощью функции math.isclose.

Он имеет следующую подпись:

isclose(a, b, rel_tol=1e-9, abs_tol=0.0)

Пример использования с абсолютной погрешностью:

from math import isclose
a = 1.0
b = 1.00000001
assert isclose(a, b, abs_tol=1e-8)

Если вы хотите с высокой точностью значимых цифр n, просто замените последнюю строку:

assert isclose(a, b, abs_tol=10**-n)

Ответ 3

Здесь взятие.

def nearly_equal(a,b,sig_fig=5):
    return ( a==b or 
             int(a*10**sig_fig) == int(b*10**sig_fig)
           )

Ответ 4

Я считаю, что ваш вопрос не определен достаточно хорошо, и приведенные в нем юнит-тесты доказывают это:

Если значение "round to N sig-fig decimal places" означает "N десятичных знаков справа от десятичной точки", тогда тест assert nearlyequal(1e9, 1e9 + 1 , 5) должен завершиться неудачей, потому что даже если вы округлите 1000000000 и 1000000001 до 0,00001 точности, они все еще отличаются.

И если по "округлить до N сиг-индексов десятичных знаков" вы имеете в виду "N самых значащих цифр, независимо от десятичной точки", тогда тест assert nearlyequal(-1e-9, 1e-9, 5) должен завершиться неудачей, потому что 0.000000001 и -0.000000001 совершенно разные, если рассматривается таким образом.

Если вы имели в виду первое определение, то первый ответ на этой странице (от Triptych) хорош. Если вы имели в виду второе определение, скажите об этом, я обещаю подумать об этом: -)

Ответ 5

Есть уже много отличных ответов, но вот подумайте:

def closeness(a, b):
  """Returns measure of equality (for two floats), in unit
     of decimal significant figures."""
  if a == b:
    return float("infinity")
  difference = abs(a - b)
  avg = (a + b)/2
  return math.log10( avg / difference )


if closeness(1000, 1000.1) > 3:
  print "Joy!"

Ответ 6

"Значимые цифры" в десятичной форме - это вопрос о настройке десятичной точки и усечении целого числа.

>>> int(3.1415926 * 10**3)
3141
>>> int(1234567 * 10**-3)
1234
>>>

Ответ 7

Это довольно распространенная проблема с числами с плавающей запятой. Я решаю его на основе обсуждения в разделе 1.5 Деммеля [1]. (1) Вычислить ошибку округления. (2) Убедитесь, что ошибка округления меньше, чем какая-либо эпсилон. Я не использовал python через некоторое время и имею только версию 2.4.3, но я постараюсь сделать это правильно.

Шаг 1. Ошибка округления

def roundoff_error(exact, approximate):
    return abs(approximate/exact - 1.0)

Шаг 2. Равномерное равенство

def float_equal(float1, float2, epsilon=2.0e-9):
    return (roundoff_error(float1, float2) < epsilon)

Есть пара очевидных недостатков с этим кодом.

  • Деление на нулевую ошибку, если точное значение равно нулю.
  • Не проверяет, что аргументы являются значениями с плавающей запятой.

Версия 1.

def roundoff_error(exact, approximate):
    if (exact == 0.0 or approximate == 0.0):
        return abs(exact + approximate)
    else:
        return abs(approximate/exact - 1.0)

def float_equal(float1, float2, epsilon=2.0e-9):
    if not isinstance(float1,float):
        raise TypeError,"First argument is not a float."
    elif not isinstance(float2,float):
        raise TypeError,"Second argument is not a float."
    else:
        return (roundoff_error(float1, float2) < epsilon)

Это немного лучше. Если либо точное, либо приблизительное значение равно нулю, то ошибка равна значению другого. Если имеется что-то помимо значения с плавающей запятой, возникает TypeError.

В этот момент единственная сложная задача - установить правильное значение для epsilon. В документации для версии 2.6.1 я заметил, что в sys.float_info есть атрибут epsilon, поэтому я бы использовал это значение по умолчанию как epsilon по умолчанию. Но правильное значение зависит как от вашего приложения, так и от вашего алгоритма.

[1] Джеймс У. Деммель, Прикладная численная линейная алгебра, SIAM, 1997.

Ответ 8

Орен Шемеш получил часть проблемы с проблемой, как указано, но там больше:

утверждают почти равные (0,0, 1е-15, 5)

также не выполняет второе определение (и это определение, которое я узнал в школе).

Независимо от того, сколько цифр вы смотрите, 0 не будет равно ненулевому. Это может стать головной болью для таких тестов, если у вас есть случай, правильный ответ которого равен нулю.

Ответ 9

Это интересное решение для Б. Доусона (с кодом на С++) на "Сравнение чисел с плавающей запятой" . Его подход основан на строгом представлении IEEE двух чисел и принудительном лексикографическом порядке, когда указанные числа представлены в виде целых чисел без знака.

Ответ 10

Существует множество способов сравнения двух чисел, чтобы узнать, согласны ли они с N значащими цифрами. Грубо говоря, вы просто хотите убедиться, что их различие меньше 10 ^ -N раз, когда сравнивается самое большое из двух чисел. Это достаточно легко.

Но что, если одно из чисел равно нулю? При сравнении против нуля вся концепция относительных различий или значащих цифр падает. Для обработки этого случая вам также необходимо иметь абсолютную разницу, которая должна быть указана иначе, чем относительная разница.

Я обсуждаю проблемы сравнения чисел с плавающей запятой, включая конкретный случай обработки нуля, в этом сообщении в блоге:

http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/