Почему python round (np.float16 (np.pi), 5) возвращает бесконечность? Ошибка, ограничение или ожидаемый? - PullRequest
5 голосов
/ 29 мая 2019

У меня есть функция Python 3 с округлением до 6 цифр (логика обрабатывает различные уровни точности).При передаче со многими (возможно, со всеми) значениями numpy.float16 он генерирует предупреждения о множественном переполнении и возвращает бесконечность.

Короткий фрагмент в заголовке вопроса или показанный ниже иллюстрирует поведение.

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

import numpy as np
x = np.float16(3.14)
x = round(x, 5)
if np.isinf(x):
    print("you've made an infinity through rounding....", 1, x)
else:
    print('just x: ', x)

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

1 Ответ

2 голосов
/ 31 мая 2019

Это ограничение алгоритма NumPy round.Я не решаюсь назвать это ошибкой: это решение для разработчиков ядра NumPy, но, тем не менее, оно может стоить сообщать.

Вот проблема: для округления до 5 десятичных знаков NumPy эквивалентно масштабированию на 100000.0, округляя до ближайшего целого числа, затем снова погружаясь на 100000.0.Это начальное масштабирование может потенциально переполниться, даже когда ожидаемый конечный результат операции round будет находиться в пределах диапазона.

Вот часть источника NumPy , где это реализовано.Вам нужно немного откатить в источнике, чтобы выяснить, что в этом случае op1 является умножением, а op2 относится к делению.

С float64 или float32 это вряд ли вызовет проблемыпотому что для нормального использования вы вряд ли окажетесь в пределах 100000.0 от верхней границы представимого диапазона типа с плавающей точкой.Но если вы подойдете слишком близко к этой верхней границе, вы увидите ту же проблему.Вот пример с np.float64:

>>> np.finfo(np.float64).max
1.7976931348623157e+308
>>> x = np.float64(1e304)  # pick something within 1e5 of that max
>>> x * 1e5
__main__:1: RuntimeWarning: overflow encountered in double_scalars
inf
>>> np.round(x, 5)  # same multiplication happening internally
/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/numpy/core/fromnumeric.py:56: RuntimeWarning: overflow encountered in multiply
  return getattr(obj, method)(*args, **kwds)
inf

И вот то же самое с float32:

>>> np.finfo(np.float32).max
3.4028235e+38
>>> x = np.float32(1e35)
>>> x * 1e5  # okay; NumPy converts to `float64`
1.0000000409184788e+40
>>> np.round(x, 5)
inf

С np.float16, это точно такая же проблема, но так какДинамический диапазон типа float16 настолько мал, что вы с большей вероятностью наблюдаете эту проблему на практике.

В целом, однако, обратите внимание, что даже если это исправить, оно равно возможно для двух аргументов round для переполнения: возможно, что исходное значение находится в диапазоне соответствующего типа с плавающей запятой, а округленное значение - нет.Вот пример с собственной функцией round Python:

>>> x = 1.76e308
>>> x
1.76e+308
>>> round(x, -307)  # should be 1.8e308, but that's out of range
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OverflowError: rounded value too large to represent

Но это может произойти только для отрицательного аргумента ndigits.Если второй аргумент был неотрицательным, то переполнения не было бы - каждое достаточно большое представимое значение в любом из стандартных типов с плавающей точкой уже является целым, поэтому round с неотрицательным ndigits не должно изменять его значение.

...