How to get Python division by -0.0 and 0.0 to result in -Inf and Inf, respectively?

大兔子大兔子 提交于 2019-12-06 20:46:12

问题


I have a situation where it is reasonable to have a division by 0.0 or by -0.0 where I would expect to see +Inf and -Inf, respectively, as results. It seems that Python enjoys throwing a

ZeroDivisionError: float division by zero

in either case. Obviously, I figured that I could simply wrap this with a test for 0.0. However, I can't find a way to distinguish between +0.0 and -0.0. (FYI you can easily get a -0.0 by typing it or via common calculations such as -1.0 * 0.0).

IEEE handles this all very nicely, but Python seems to take pains to hide the well thought out IEEE behavior. In fact, the fact that 0.0 == -0.0 is actually an IEEE feature so Python's behavior seriously breaks things. It works perfectly well in C, Java, Tcl, and even JavaScript.

Suggestions?


回答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



回答2:


I completely agree with @Mark Ransom, except that I would use try instead:

def f(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return copysign(float('inf'), denominator)

The reason I recommend this is that if you are performing this function many times, you don't have to waste time each iteration checking if the value is zero before you attempt the division.

EDIT:

I have compared the speed of the try compared to the if function:

def g(a, b):
    if b == 0:
        return copysign(float('inf'), b)
    else:
        return a / b

Here is the tests:

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

Here is the result:

try: 0.573683023453
if: 0.610251903534

This indicates the try method is faster, at least on my machine.




回答3:


The gmpy2 library provides an arbitrary precision floating type and also allows you to control IEEE-754 exception behavior.

>>> 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

Disclaimer: I maintain gmpy2.




回答4:


Here is a solution that handles all the edge cases correctly, at least as far as I'm aware of:

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))

In the case that b is a zero, it basically splits the division a/b into a * (1/b) and implements 1/b via copysign(). The multiplication does not throw when its arguments are 0*inf, instead it correctly yields a NAN.



来源:https://stackoverflow.com/questions/16226633/how-to-get-python-division-by-0-0-and-0-0-to-result-in-inf-and-inf-respective

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!