Python Infinity - Bất kỳ hãy cẩn thận?


179

Vì vậy, Python có vô cùng tích cực và tiêu cực:

float("inf"), float("-inf")

Đây chỉ là loại tính năng phải có một số cảnh báo. Có bất cứ điều gì tôi nên nhận thức?


25
Lưu ý rằng hằng số 1e309sẽ được hiểu là +inf-1e309sẽ được hiểu là -inf.
Chris Taylor

Câu trả lời:


97

Bạn vẫn có thể nhận các giá trị không phải là số (NaN) từ số học đơn giản liên quan đến inf:

>>> 0 * float("inf")
nan

Lưu ý rằng thông thường bạn sẽ không nhận được infgiá trị thông qua các phép tính số học thông thường:

>>> 2.0**2
4.0
>>> _**2
16.0
>>> _**2
256.0
>>> _**2
65536.0
>>> _**2
4294967296.0
>>> _**2
1.8446744073709552e+19
>>> _**2
3.4028236692093846e+38
>>> _**2
1.157920892373162e+77
>>> _**2
1.3407807929942597e+154
>>> _**2
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
OverflowError: (34, 'Numerical result out of range')

Các infgiá trị được coi là một giá trị rất đặc biệt với ngữ nghĩa không bình thường, vì vậy nó là tốt hơn để biết về một OverflowErrorngay lập tức thông qua một ngoại lệ, chứ không phải có một infgiá trị âm thầm tiêm vào tính toán của bạn.


8
Một phép cộng float đơn giản, phép nhân, v.v ... sẽ vui vẻ tạo ra inf mặc dù: f = 1.3407807929942597e + 154; f * f => inf. Có vẻ như là một ngoại lệ của ** để tăng OverflowError.
eregon

@eregon, thực sự, **có vẻ như một lỗi. Khi nó tràn ra với các số thực, nó sẽ báo lỗi, nhưng khi bất kỳ toán hạng nào của nó là infhoặc -inf, nó sẽ trả về 0.0hoặc inf. Vì vậy, nó không làm việc một cách chính xác khi đầu vào là inifinty, nhưng không phải khi kết quả nên vô cùng.
Abel

2
@Abel Đó không phải là lỗi. Tràn ngập có nghĩa là số lượng rất lớn. Quá lớn để đại diện cho nó, nhưng vẫn còn xa nhỏ hơn vô cùng. Đặt vô hạn vào một nơi như vậy có thể hữu ích cho trình xử lý ngoại lệ của logic ứng dụng cụ thể của bạn, nhưng nói chung sẽ không chính xác đối với Python.
Lutz Prechelt 30/03/2016

6
@Lutz nếu nó xuất hiện dưới dạng nhân thì đó vẫn là hành vi không nhất quán. Chắc chắn lớn * lớn cũng không phải là vô cùng.
Richard Rast

100

Việc triển khai của Python tuân theo tiêu chuẩn IEEE-754 khá tốt, bạn có thể sử dụng như một hướng dẫn, nhưng nó phụ thuộc vào hệ thống cơ bản mà nó được biên dịch, do đó có thể xảy ra sự khác biệt về nền tảng . Gần đây, một bản sửa lỗi đã được áp dụng cho phép "vô cực" cũng như "inf" , nhưng đó là tầm quan trọng nhỏ ở đây.

Các phần sau đây cũng áp dụng tốt cho bất kỳ ngôn ngữ nào thực hiện chính xác số học dấu phẩy động của IEEE, nó không dành riêng cho Python.

So sánh bất đẳng thức

Khi giao dịch với vô cực và lớn hơn >hoặc nhỏ hơn <toán tử, các giá trị sau:

  • bất kỳ số nào bao gồm +infcao hơn-inf
  • bất kỳ số nào bao gồm -infthấp hơn+inf
  • +infkhông cao cũng không thấp hơn+inf
  • -inf không cao hơn cũng không thấp hơn -inf
  • mọi so sánh liên quan NaNlà sai ( infkhông cao hơn, cũng không thấp hơn NaN)

So sánh cho bình đẳng

Khi so sánh cho bình đẳng, +inf+infbằng nhau, như là -inf-inf. Đây là một vấn đề gây tranh cãi và có thể gây tranh cãi với bạn, nhưng đó là trong tiêu chuẩn IEEE và Python hành xử giống như vậy.

Tất nhiên, +inflà không đồng đều -infvà tất cả mọi thứ, bao gồm cả NaNchính nó, là không đồng đều NaN.

Tính toán với vô cùng

Hầu hết các phép tính với vô cực sẽ mang lại vô hạn, trừ khi cả hai toán hạng là vô cực, khi phân chia thao tác hoặc modulo, hoặc nhân với số 0, có một số quy tắc đặc biệt cần ghi nhớ:

  • khi nhân với 0, kết quả không được xác định, nó mang lại NaN
  • khi chia bất kỳ số nào (trừ vô cực) cho vô cùng, sẽ sinh ra 0.0hoặc -0.0².
  • khi chia (bao gồm modulo) vô cực dương hoặc âm cho vô cực dương hoặc âm, kết quả không được xác định, vì vậy NaN.
  • khi trừ, kết quả có thể gây ngạc nhiên, nhưng tuân theo ý nghĩa toán học thông thường :
    • khi làm inf - inf, kết quả không xác định : NaN;
    • khi làm inf - -inf, kết quả là inf;
    • khi làm -inf - inf, kết quả là -inf;
    • khi làm -inf - -inf, kết quả không xác định : NaN.
  • khi thêm vào, nó cũng có thể gây ngạc nhiên tương tự:
    • khi làm inf + inf, kết quả là inf;
    • khi làm inf + -inf, kết quả không xác định : NaN;
    • khi làm -inf + inf, kết quả không xác định : NaN;
    • khi làm -inf + -inf, kết quả là -inf.
  • sử dụng math.pow, powhoặc **là khó khăn, vì nó không hoạt động như bình thường. Nó đưa ra một ngoại lệ tràn khi kết quả có hai số thực quá cao để phù hợp với số float chính xác kép (nó sẽ trả về vô cực), nhưng khi đầu vào là infhoặc -inf, nó sẽ hoạt động chính xác và trả về infhoặc 0.0. Khi đối số thứ hai là NaN, nó trả về NaN, trừ khi đối số thứ nhất là 1.0. Có nhiều vấn đề hơn, không phải tất cả được đề cập trong các tài liệu .
  • math.expchịu những vấn đề tương tự như math.pow. Một giải pháp để khắc phục điều này cho tràn là sử dụng mã tương tự như sau:

    try:
        res = math.exp(420000)
    except OverflowError:
        res = float('inf')

Ghi chú

Lưu ý 1: như một cảnh báo bổ sung, như được xác định bởi tiêu chuẩn IEEE, nếu kết quả tính toán của bạn dưới hoặc tràn, kết quả sẽ không phải là lỗi dưới hoặc tràn, mà là vô cực dương hoặc âm: 1e308 * 10.0mang lại inf.

Lưu ý 2: bởi vì bất kỳ phép tính nào có NaNtrả về NaNvà bất kỳ so sánh nào NaN, kể cả NaNchính nó false, bạn nên sử dụng math.isnanhàm để xác định xem một số có thực sự không NaN.

Lưu ý 3: mặc dù Python hỗ trợ viết float('-NaN'), nhưng dấu hiệu bị bỏ qua, vì không tồn tại dấu hiệu NaNbên trong. Nếu bạn chia -inf / +inf, kết quả là NaN, không -NaN(không có điều đó).

Lưu ý 4: hãy cẩn thận dựa vào bất kỳ điều nào ở trên, vì Python dựa vào thư viện C hoặc Java mà nó được biên dịch và không phải tất cả các hệ thống cơ bản đều thực hiện chính xác tất cả hành vi này. Nếu bạn muốn chắc chắn, hãy kiểm tra vô hạn trước khi thực hiện các tính toán của bạn.

¹) Gần đây có nghĩa là từ phiên bản 3.2 .
²) Điểm nổi hỗ trợ số 0 dương và âm, vì vậy: x / float('inf')giữ nguyên dấu hiệu và -1 / float('inf')sản lượng -0.0, 1 / float(-inf)sản lượng -0.0, 1 / float('inf')sản lượng 0.0-1/ float(-inf)sản lượng 0.0. Ngoài ra, 0.0 == -0.0true , bạn phải kiểm tra thủ công dấu hiệu nếu bạn không muốn nó là sự thật.


11
Một nitlog nhỏ: không phải mọi phép tính với vô cực đều mang lại vô hạn:-1 * float('infinity') == -inf
Evan Krall

4
Đó là lý do tại sao tôi nói nó là một nitlog nhỏ . Bạn đã làm tôi lo lắng trong một phút rằng dấu hiệu đó sẽ hoàn toàn bị bỏ qua khi làm việc với vô hạn, và tôi muốn làm rõ cho những người khác.
Evan Krall

12
Chà, gần: 1 / float ('vô cùng') == 0,0
Phil

3
@Phil: Mặc dù tôi khá chắc chắn rằng bạn chỉ đang cố gắng chứng minh rằng không phải tất cả các phép tính với inf đều dẫn đến inf hoặc NaN, tôi chỉ muốn làm rõ cho những người khác có thể đang đọc qua các bình luận, rằng 1 / float ('vô cùng ') == 0,0 là đúng; vì, khi bạn đang đến gần vô cùng, kết quả của phép chia tiếp cận 0. Tôi biết đó chỉ là phép tính cơ bản, nhưng tôi muốn chắc chắn rằng những người đọc hiểu, hoặc ít nhất là có manh mối về lý do, kết quả là gì.
Anthony Pace

1
Tôi có cảm giác rằng câu trả lời này tốt hơn nhiều so với câu trả lời được chấp nhận.
Christian Herenz

3

C99 cũng vậy .

Biểu diễn điểm nổi IEEE 754 được sử dụng bởi tất cả các bộ xử lý hiện đại có một số mẫu bit đặc biệt dành riêng cho vô cực dương (dấu = 0, exp = ~ 0, frac = 0), vô cực âm (dấu = 1, exp = ~ 0, frac = 0 ) và nhiều NaN (Không phải là Số: exp = ~ 0, frac ≠ 0).

Tất cả những gì bạn cần lo lắng: một số số học có thể gây ra ngoại lệ / bẫy dấu phẩy động, nhưng chúng không bị giới hạn chỉ với các hằng số "thú vị" này.


1
Vì vậy, nếu số học của tôi quá lớn, nó có thể trở thành một inf?
Casebash

@Casebash Không, nó sẽ gây ra một OverflowError.
wizzwizz4

2

Tôi tìm thấy một cảnh báo mà cho đến nay không ai đề cập đến. Tôi không biết liệu nó có xuất hiện thường xuyên trong các tình huống thực tế không, nhưng ở đây là vì sự hoàn thiện.

Thông thường, việc tính toán một số vô cực modulo sẽ trả về chính nó như một số float, nhưng một phần vô cực modulo sẽ trả về nan(không phải là một số). Đây là một ví dụ:

>>> from fractions import Fraction
>>> from math import inf
>>> 3 % inf
3.0
>>> 3.5 % inf
3.5
>>> Fraction('1/3') % inf
nan

Tôi đã gửi một vấn đề trên trình theo dõi lỗi Python. Nó có thể được nhìn thấy tại https://bugs.python.org/su32968 .

Cập nhật: điều này sẽ được sửa trong Python 3.8 .


2

MỘT CUỘC CÁCH MẠNG RẤT RẤT: Division by Zero

trong một 1/xphân số, tùy thuộc vào x = 1e-323infnhưng khi x = 1e-324hay ít nó némZeroDivisionError

>>> 1/1e-323
inf

>>> 1/1e-324
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: float division by zero

vì vậy hãy thận trọng

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.