Ответ 1
from math import copysign
def divide(numerator, denominator):
if denominator == 0.0:
return copysign(float('inf'), denominator)
return numerator / denominator
>>> divide(1, -0.0)
-inf
>>> divide(1, 0)
inf
У меня есть ситуация, когда разумно иметь деление на 0,0 или на -0,0, где я бы ожидал увидеть + Inf и -Inf, соответственно, как результаты. Кажется, что Python любит бросать
ZeroDivisionError: float division by zero
в любом случае. Очевидно, я решил, что могу просто обернуть это тестом на 0.0. Однако я не могу найти способ отличить от +0.0 до -0.0. (FYI вы можете легко получить -0.0, набрав его или с помощью общих вычислений, таких как -1.0 * 0.0).
IEEE обрабатывает все это очень хорошо, но Python, похоже, прилагает все усилия, чтобы скрыть хорошо продуманное поведение IEEE. Фактически, тот факт, что 0.0 == -0.0 на самом деле является функцией IEEE, поэтому поведение Python серьезно нарушает ситуацию. Он отлично работает в C, Java, Tcl и даже JavaScript.
Предложения?
from math import copysign
def divide(numerator, denominator):
if denominator == 0.0:
return copysign(float('inf'), denominator)
return numerator / denominator
>>> divide(1, -0.0)
-inf
>>> divide(1, 0)
inf
Я полностью согласен с @Mark Ransom, за исключением того, что вместо этого я использовал бы try
:
def f(a, b):
try:
return a / b
except ZeroDivisionError:
return copysign(float('inf'), denominator)
Причина, по которой я рекомендую это, заключается в том, что если вы выполняете эту функцию много раз, вам не нужно тратить время на каждую проверку итерации, если значение равно нулю, прежде чем пытаться делить.
ИЗМЕНИТЬ
Я сравнивал скорость try
по сравнению с функцией if
:
def g(a, b):
if b == 0:
return copysign(float('inf'), b)
else:
return a / b
Вот тесты:
s = time.time()
[f(10, x) for x in xrange(-1000000, 1000000, 1)]
print 'try:', time.time()-s
s = time.time()
[g(10, x) for x in xrange(-1000000, 1000000, 1)]
print 'if:', time.time()-s
Вот результат:
try: 0.573683023453
if: 0.610251903534
Это означает, что метод try
работает быстрее, по крайней мере, на моей машине.
Библиотека gmpy2 предоставляет плавающий тип произвольной точности, а также позволяет управлять поведением IEEE-754.
>>> import gmpy2
>>> from gmpy2 import mpfr
>>> mpfr(1)/mpfr(0)
mpfr('inf')
>>> mpfr(1)/mpfr(-0)
mpfr('inf')
>>> mpfr(1)/mpfr("-0")
mpfr('-inf')
>>> gmpy2.get_context().trap_divzero=True
>>> mpfr(1)/mpfr(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
gmpy2.DivisionByZeroError: 'mpfr' division by zero in division
Отказ от ответственности: я поддерживаю gmpy2.
Вот решение, которое правильно обрабатывает все крайние случаи, по крайней мере, насколько я знаю:
def divide(a: float, b: float) -> float:
try:
return a/b
except:
return a*math.copysign(math.inf, b)
assert divide( 1, 1) == 1
assert divide( 1, -1) == -1
assert divide(-1, 1) == -1
assert divide(-1, -1) == 1
assert divide( 1, 0.0) > 1e300
assert divide( 1, -0.0) < -1e300
assert divide(-1, 0.0) < -1e300
assert divide(-1, -0.0) > 1e300
assert math.isnan(divide( 0.0, 0.0))
assert math.isnan(divide( 0.0, -0.0))
assert math.isnan(divide(-0.0, 0.0))
assert math.isnan(divide(-0.0, -0.0))
В случае, когда b
- ноль, он в основном разделяет деление a/b
на a * (1/b)
и реализует 1/b
через copysign()
. Умножение не выбрасывает, когда его аргументы равны 0*inf
, вместо этого оно правильно возвращает NAN.