Как проверить, находится ли ввод функции в пределах предела типа данных? - PullRequest
0 голосов
/ 26 ноября 2018

У меня есть функция, которая принимает аргумент array-like и аргумент value в качестве входных данных.Во время модульных тестов этой функции (я использую hypothesis), если выдается очень большой value (тот, который не может быть обработан с помощью np.float128), функция завершается ошибкой.

Какой хороший способ обнаружить такие значения и правильно их обработать?

Ниже приведен код моей функции:

def find_nearest(my_array, value):
    """ Find the nearest value in an unsorted array.
    """
    # Convert to numpy array and drop NaN values.
    my_array = np.array(my_array, copy=False, dtype=np.float128)
    my_array = my_array[~np.isnan(my_array)]

    return my_array[(np.abs(my_array - value)).argmin()]

Пример, который выдает ошибку:

find_nearest([0.0, 1.0], 1.8446744073709556e+19)

Броски: 0.0, но правильный ответ - 1.0.

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

Ответы [ 2 ]

0 голосов
/ 26 ноября 2018

Проблема здесь, похоже, не в том, что float128 не может обработать 1.844...e+19, а скорее в том, что вы, вероятно, не можете добавить два числа с плавающей запятой с такими радикально разными масштабами и ожидать получения точных результатов:

In [1]: 1.8446744073709556e+19 - 1.0 == 1.8446744073709556e+19
Out[1]: True

Если вы действительно нуждаетесь в этом значении точности, лучше всего было бы использовать десятичные объекты и помещать их в массивный массив как dtype 'object':

In [1]: from decimal import Decimal

In [2]: big_num = Decimal(1.8446744073709556e+19)

In [3]: big_num  # Note the slight innaccuracies due to floating point conversion
Out[3]: Decimal('18446744073709555712')

In [4]: a = np.array([Decimal(0.0), Decimal(1.0)], dtype='object')

In [5]: a[np.abs(a - big_num).argmin()]
Out[5]: Decimal('1')

Обратите внимание, что это будет НАМНОГО медленнее, чем типичные операции Numpy, потому что он должен возвращаться к Python для каждого вычисления, а не использовать собственные оптимизированные библиотеки (поскольку numpy не имеет десятичного типа).

РЕДАКТИРОВАТЬ:

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

fails = len(set(my_array)) == len(set(my_array - value))

Этогарантирует, что при вычитании value и уникального числа X в my_array вы получите уникальный результат.Это, как правило, истинный факт о вычитании, и если он терпит неудачу, то это потому, что арифметика с плавающей запятой не достаточно точна для обработки value - X как числа, отличного от value или X.

0 голосов
/ 26 ноября 2018

Осторожно, float128 на самом деле не 128-битная точность!На самом деле это реализация longdouble: https://en.wikipedia.org/wiki/Extended_precision. Точность хранилища такого типа составляет 63 бита - поэтому он дает сбой около 1e + 19, потому что для вас это 63 бинарных бита.Конечно, если разница в вашем массиве больше 1, он сможет различить это по этому числу, это просто означает, что любая разница, которую вы пытаетесь выделить, должна быть больше 1/2**63 вашего ввода value.

Какова внутренняя точность numpy.float128? Вот старый ответ, который развивает то же самое.Я сделал свой тест и подтвердил, что np.float128 - это точно longdouble с точностью 63 бита.

Я предлагаю вам установить максимум для value, и если ваше значение больше этоголибо:

  1. уменьшите значение до этого числа при условии, что все в вашем массиве будет меньше этого числа.

  2. Сгенерируйте ошибку.

примерно так:

VALUE_MAX = 1e18
def find_nearest(my_array, value):
    if value > VALUE_MAX:
        value = VALUE_MAX
    ...

В качестве альтернативы вы можете выбрать более научный подход, например, фактически сравнивая ваш value с максимумоммассив:

def find_nearest(my_array, value):
    my_array = np.array(my_array, dtype=np.float128)
    if value > np.amax(my_array):
        value = np.amax(my_array)
    elif value < np.amin(my_array):
        value = np.amin(my_array)
    ...

Таким образом, вы будете уверены, что никогда не столкнетесь с этой проблемой - поскольку ваше значение всегда будет максимально равно максимуму вашего массива или минимуму как минимум вашего массива.

...