Это ограничение алгоритма 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
не должно изменять его значение.