除以零等于零

问题描述 投票:0回答:10

我怎样才能忽略

ZeroDivisionError
并做出
n / 0 == 0

python division zero
10个回答
141
投票

除法前检查分母是否为零。这避免了捕获异常的开销,如果您希望大量除以零,这可能会更有效。

def weird_division(n, d):
    return n / d if d else 0

50
投票

解决方案

当您想要高效处理

ZeroDivisionError
(除以零)时,您不应该使用异常或条件。

result = b and a / b or 0  # a / b

它是如何运作的?

  • b != 0
    时,我们有
    True and a / b or 0
    True and a / b
    等于
    a / b
    a / b or 0
    等于
    a / b
  • b == 0
    时,我们有
    False and a / b or 0
    False and a / b
    等于
    False
    False or 0
    等于
    0

基准

Timer unit: 1e-06 s

Total time: 118.362 s
File: benchmark.py
Function: exception_div at line 3

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     3                                           @profile
     4                                           def exception_div(a, b):
     5 100000000   23419098.5      0.2     19.8      try:
     6 100000000   40715642.9      0.4     34.4          return a / b
     7 100000000   28910860.8      0.3     24.4      except ZeroDivisionError:
     8 100000000   25316209.7      0.3     21.4          return 0

Total time: 23.638 s
File: benchmark.py
Function: conditional_div at line 10

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    10                                           @profile
    11                                           def conditional_div(a, b):
    12 100000000   23638033.3      0.2    100.0      return a / b if b else 0

Total time: 23.2162 s
File: benchmark.py
Function: logic_div at line 14

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
    14                                           @profile
    15                                           def logic_div(a, b):
    16 100000000   23216226.0      0.2    100.0      return b and a / b or 0

45
投票

您可以使用

try
/
except
块来实现此目的。

def foo(x,y):
    try:
        return x/y
    except ZeroDivisionError:
        return 0

>>> foo(5,0)
0

>>> foo(6,2)
3.0

20
投票

我认为

try
except
(如 Cyber 的回答)通常是最好的方法(而且更Pythonic:请求宽恕比请求许可更好!),但这是另一个:

def safe_div(x,y):
    if y == 0:
        return 0
    return x / y

支持这样做的一个论点是,如果你期望

ZeroDivisionError
经常发生,那么提前检查 0 分母会快很多(这是 python 3):

import time

def timing(func):
    def wrap(f):
        time1 = time.time()
        ret = func(f)
        time2 = time.time()
        print('%s function took %0.3f ms' % (f.__name__, int((time2-time1)*1000.0)))
        return ret
    return wrap

def safe_div(x,y):
    if y==0: return 0
    return x/y

def try_div(x,y):
    try: return x/y
    except ZeroDivisionError: return 0

@timing
def test_many_errors(f):
    print("Results for lots of caught errors:")
    for i in range(1000000):
        f(i,0)

@timing
def test_few_errors(f):
    print("Results for no caught errors:")
    for i in range(1000000):
        f(i,1)

test_many_errors(safe_div)
test_many_errors(try_div)
test_few_errors(safe_div)
test_few_errors(try_div)

输出:

Results for lots of caught errors:
safe_div function took 185.000 ms
Results for lots of caught errors:
try_div function took 727.000 ms
Results for no caught errors:
safe_div function took 223.000 ms
Results for no caught errors:
try_div function took 205.000 ms

因此,对于很多(或实际上是全部)错误,使用

try
except
的速度会慢 3 到 4 倍;也就是说:对于捕获错误的迭代来说,速度要慢 3 到 4 倍。当错误很少(或者实际上没有)时,使用 if 语句的版本会稍微慢一些(10% 左右)。
    


12
投票


3
投票

(if Y Z=X/Y else Z=0)



2
投票

if y !=0 : z = x/y else: z = 0

或者你可以使用:

z = ( x / y ) if y != 0 else 0

如果您尝试除以两个整数列表,您可以使用:

z = [j/k if k else 0 for j, k in zip(x, y)]

这里,x 和 y 是两个整数列表。


2
投票

def safe_division(numerator, denominator): """Return 0 if denominator is 0.""" return denominator and numerator / denominator

and

 返回零 
denominator 或除法结果。
字节码:

In [51]: dis(safe_division) 25 0 LOAD_FAST 1 (denominator) 2 JUMP_IF_FALSE_OR_POP 5 (to 10) 4 LOAD_FAST 0 (numerator) 6 LOAD_FAST 1 (denominator) 8 BINARY_TRUE_DIVIDE >> 10 RETURN_VALUE

返回零 
denominator

无需使用

LOAD_CONST
加载零。

如果您不介意卷起袖子来获得更多性能,这里有一个 C++ 版本:

static inline double div_positive_or_0(double a, double b) noexcept { // Convert the quotient of division by non-positive numbers to 0. // vpandpd turns quotient to 0 without branches. // Using Intel intrinsincs to convert double to __m128d involves extra register copies. // Use inline assembly to avoid unnecessary register copying. a /= b; asm("vcmpltsd %[b], %[z], %[b] \n\t" "vandpd %[a], %[a], %[b]" :[a]"+v"(a),[b]"+v"(b) :[z]"v"(_mm_setzero_pd()) :); return a; }



1
投票

x=0,y=0 print (y/(x or not x))

输出:

>>>x=0 >>>y=0 >>>print(y/(x or not x)) 0.0 >>>x =1000 >>>print(y/(x or not x)) 0.000999000999000999

如果 x 不等于 0,not x 将为 false,因此此时它会除以实际的 x。


0
投票
ToTomire

的解决方案会更快。如果感觉 conditional_div 通常应该因其自然语言可读性而被首选,但如果我能准确理解为什么

logic_div
更快,这可能会在将来对我有所帮助。我为此寻找了 python 的
dis
>>> conditional_div = lambda n,d: n/d if d else 0
>>> logic_div = lambda n,d: d and n/d or 0
>>> dis.dis(conditional_div)
  1           0 LOAD_FAST                1 (d)
              2 POP_JUMP_IF_FALSE       12
              4 LOAD_FAST                0 (n)
              6 LOAD_FAST                1 (d)
              8 BINARY_TRUE_DIVIDE
             10 RETURN_VALUE
        >>   12 LOAD_CONST               1 (0)
             14 RETURN_VALUE
>>> dis.dis(logic_div)
  1           0 LOAD_FAST                1 (d)
              2 POP_JUMP_IF_FALSE       12
              4 LOAD_FAST                0 (n)
              6 LOAD_FAST                1 (d)
              8 BINARY_TRUE_DIVIDE
             10 JUMP_IF_TRUE_OR_POP     14
        >>   12 LOAD_CONST               1 (0)
        >>   14 RETURN_VALUE

而且看起来
logic_div

实际上应该有一个额外的步骤。直到“8”,两个字节码都是相同的。在“10”时,

conditional_div
只会返回一个值,而
logic_div
必须在为真时进行跳转,然后返回。也许替代方案
..._OR_POP
比返回更快,所以在一定程度上它的最后一步会更短?但激活
..._OR_POP
的唯一方法是分子为零且分母非零。当分母为零时,两个字节码采用相同的路线。这感觉不是一个令人满意的结论。如果我误解了什么,也许有人可以解释一下。
供参考

>>> sys.version '3.9.6 (tags/v3.9.6:db3ff76, Jun 28 2021, 15:26:21) [MSC v.1929 64 bit (AMD64)]'

© www.soinside.com 2019 - 2024. All rights reserved.