Cách tốt nhất để so sánh số float cho gần như bằng nhau trong Python là gì?


330

Người ta biết rằng so sánh phao cho sự bình đẳng là một chút khó khăn do các vấn đề làm tròn và chính xác.

Ví dụ: https://randomascii.wordpress.com/2012/02/25/compared-floating-point-numbers-2012-edition/

Cách được đề xuất để đối phó với điều này trong Python là gì?

Chắc chắn có một chức năng thư viện tiêu chuẩn cho điều này ở đâu đó?


@tolomea: Vì nó phụ thuộc vào ứng dụng của bạn và dữ liệu của bạn và miền vấn đề của bạn - và đó chỉ là một dòng mã - tại sao lại có "chức năng thư viện chuẩn"?
S.Lott

9
@ S. Lott: all, any, max, minlà mỗi cơ bản một lớp lót, và họ không chỉ được cung cấp trong một thư viện, chúng được xây dựng trong các chức năng. Vì vậy, lý do của BDFL không phải là điều đó. Một dòng mã mà hầu hết mọi người viết là khá phức tạp và thường không hoạt động, đó là một lý do mạnh mẽ để cung cấp một cái gì đó tốt hơn. Tất nhiên, bất kỳ mô-đun nào cung cấp các chiến lược khác cũng sẽ phải cung cấp các cảnh báo khi chúng phù hợp và quan trọng hơn là khi chúng không hoạt động. Phân tích số rất khó, không có gì đáng thất vọng khi các nhà thiết kế ngôn ngữ thường không thử các công cụ để trợ giúp.
Steve Jessop

@Steve Jessop. Các hàm định hướng bộ sưu tập đó không có các phụ thuộc miền ứng dụng, dữ liệu và vấn đề mà float-point thực hiện. Vì vậy, "một lớp lót" rõ ràng không quan trọng bằng lý do thực sự. Phân tích số là khó, và không thể là một phần hạng nhất của thư viện ngôn ngữ mục đích chung.
S.Lott

6
@ S.Lott: Có lẽ tôi đồng ý nếu bản phân phối Python tiêu chuẩn không đi kèm với nhiều mô-đun cho giao diện XML. Rõ ràng thực tế là các ứng dụng khác nhau cần phải làm một cái gì đó khác nhau hoàn toàn không có gì để đặt các mô-đun vào cơ sở được thiết lập để làm theo cách này hay cách khác. Chắc chắn có những thủ thuật để so sánh những chiếc phao được sử dụng lại rất nhiều, cơ bản nhất là một số lượng ulps được chỉ định. Vì vậy, tôi chỉ đồng ý một phần - vấn đề là phân tích số là khó. Về nguyên tắc, Python có thể cung cấp các công cụ để làm cho nó dễ dàng hơn, đôi khi. Tôi đoán không có ai tình nguyện.
Steve Jessop

4
Ngoài ra, "nó có một dòng mã khó thiết kế" - nếu nó vẫn là một lớp lót một khi bạn thực hiện đúng cách, tôi nghĩ màn hình của bạn rộng hơn của tôi ;-). Dù sao, tôi nghĩ rằng toàn bộ khu vực này là khá chuyên biệt, theo nghĩa là hầu hết các lập trình viên (bao gồm cả tôi) rất hiếm khi sử dụng nó. Kết hợp với việc khó khăn, nó sẽ không đạt được danh sách "mong muốn nhất" cho các thư viện cốt lõi trong hầu hết các ngôn ngữ.
Steve Jessop

Câu trả lời:


322

Python 3.5 thêm math.isclosevà các cmath.isclosechức năng như được mô tả trong PEP 485 .

Nếu bạn đang sử dụng phiên bản Python trước đó, chức năng tương đương sẽ được cung cấp trong tài liệu .

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

rel_tollà một dung sai tương đối, nó được nhân với độ lớn hơn của hai đối số; khi các giá trị trở nên lớn hơn, thì sự khác biệt được phép giữa chúng trong khi vẫn xem xét chúng bằng nhau.

abs_tollà một dung sai tuyệt đối được áp dụng nguyên trạng trong mọi trường hợp. Nếu chênh lệch nhỏ hơn một trong hai dung sai đó, các giá trị được coi là bằng nhau.


26
lưu ý khi ahoặc blà một numpy array, numpy.isclosehoạt động.
dbliss

6
@marsh rel_tollà một dung sai tương đối , nó được nhân với độ lớn hơn của hai đối số; khi các giá trị trở nên lớn hơn, thì sự khác biệt được phép giữa chúng trong khi vẫn xem xét chúng bằng nhau. abs_tollà một dung sai tuyệt đối được áp dụng nguyên trạng trong mọi trường hợp. Nếu chênh lệch nhỏ hơn một trong hai dung sai đó, các giá trị được coi là bằng nhau.
Đánh dấu tiền chuộc

5
Không làm giảm giá trị của câu trả lời này (tôi nghĩ đó là một câu trả lời hay), đáng chú ý là tài liệu cũng nói: "Kiểm tra lỗi modulo, v.v., hàm sẽ trả về kết quả của ..." Nói cách khác, isclosehàm (ở trên) không phải là một thực hiện đầy đủ .
rkersh

5
Xin lỗi vì đã làm sống lại một chủ đề cũ, nhưng có vẻ như đáng để chỉ ra rằng iscloseluôn tuân thủ các tiêu chí ít bảo thủ hơn . Tôi chỉ đề cập đến nó bởi vì hành vi đó là phản trực giác đối với tôi. Tôi đã chỉ định hai tiêu chí, tôi sẽ luôn mong muốn dung sai nhỏ hơn sẽ thay thế lớn hơn.
Mackie Messer

3
@MackieMesser bạn có quyền theo ý kiến ​​của bạn, nhưng hành vi này hoàn toàn có ý nghĩa với tôi. Theo định nghĩa của bạn, không có gì có thể là "gần bằng", bởi vì dung sai tương đối nhân với số 0 luôn bằng không.
Đánh dấu tiền chuộc

71

Là một cái gì đó đơn giản như sau không đủ tốt?

return abs(f1 - f2) <= allowed_error

8
Như liên kết tôi đã cung cấp chỉ ra, phép trừ chỉ hoạt động nếu bạn biết trước độ lớn gần đúng của các số.
Gordon Wrigley

8
Theo kinh nghiệm của tôi, phương pháp tốt nhất để so sánh phao là : abs(f1-f2) < tol*max(abs(f1),abs(f2)). Loại dung sai tương đối này là cách có ý nghĩa duy nhất để so sánh phao nói chung, vì chúng thường bị ảnh hưởng bởi lỗi vòng trong các số thập phân nhỏ.
Sesquipedal 4/2/2015

2
Chỉ cần thêm một ví dụ đơn giản tại sao nó có thể không hoạt động : >>> abs(0.04 - 0.03) <= 0.01, nó mang lại False. Tôi sử dụngPython 2.7.10 [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
schatten

3
@schatten để công bằng, ví dụ đó có liên quan nhiều đến độ chính xác / định dạng nhị phân của máy hơn là so sánh cụ thể. Khi bạn nhập 0,03 vào hệ thống, đó không thực sự là con số tạo ra CPU.
Andrew White

2
@AndrewWhite ví dụ đó cho thấy rằng abs(f1 - f2) <= allowed_errorkhông hoạt động như mong đợi.
schatten

44

Tôi đồng ý rằng câu trả lời của Gareth có lẽ thích hợp nhất là một chức năng / giải pháp nhẹ.

Nhưng tôi nghĩ sẽ hữu ích khi lưu ý rằng nếu bạn đang sử dụng NumPy hoặc đang xem xét nó, có một chức năng đóng gói cho việc này.

numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)

Mặc dù có một chút từ chối: cài đặt NumPy có thể là một trải nghiệm không hề nhỏ tùy thuộc vào nền tảng của bạn.


1
"Cài đặt numpy có thể là một trải nghiệm không hề nhỏ tùy thuộc vào nền tảng của bạn." ... um Gì? Những nền tảng nào là "không tầm thường" để cài đặt numpy? Chính xác những gì làm cho nó không tầm thường?
Giăng

10
@ John: khó có được nhị phân 64 bit cho Windows. Khó để có được numpy thông qua piptrên Windows.
Ben Bolker 6/03/2015

@Ternak: Tôi có, nhưng một số sinh viên của tôi sử dụng Windows, vì vậy tôi phải đối phó với những thứ này.
Ben Bolker

4
@BenBolker Nếu bạn phải cài đặt nền tảng khoa học dữ liệu mở được cung cấp bởi Python, cách tốt nhất là Anaconda continuum.io/doads (gấu trúc, numpy và nhiều thứ khác)
jrivergno

Cài đặt Anaconda là không quan trọng
endolith

13

Sử dụng decimalmô-đun của Python , cung cấp Decimallớp.

Từ các ý kiến:

Điều đáng chú ý là nếu bạn đang làm công việc nặng về toán học và bạn hoàn toàn không cần độ chính xác từ số thập phân, thì điều này thực sự có thể làm hỏng mọi thứ. Phao là cách, cách nhanh hơn để đối phó, nhưng không chính xác. Số thập phân cực kỳ chính xác nhưng chậm.


11

Tôi không biết bất cứ điều gì trong thư viện chuẩn Python (hoặc ở nơi khác) thực hiện AlmostEqual2sComplementchức năng của Dawson . Nếu đó là loại hành vi bạn muốn, bạn sẽ phải tự thực hiện. (Trong trường hợp đó, thay vì sử dụng các cách hack bitwise thông minh của Dawson, có lẽ bạn nên sử dụng các thử nghiệm thông thường hơn về hình thức if abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2hoặc tương tự. Để có được hành vi giống Dawson, bạn có thể nói điều gì đó giống như if abs(a-b) <= eps*max(EPS,abs(a),abs(b))đối với một số lỗi nhỏ EPS; giống như Dawson, nhưng nó giống nhau về tinh thần.


Tôi không hoàn toàn làm theo những gì bạn đang làm ở đây, nhưng nó rất thú vị. Sự khác biệt giữa eps, eps1, eps2 và EPS là gì?
Gordon Wrigley

eps1eps2xác định một mức độ tương đối và dung sai tuyệt đối: bạn sẵn sàng cho phép abkhác biệt về khoảng eps1thời gian chúng cộng với nhau eps2. epslà một dung sai duy nhất; bạn đã sẵn sàng cho phép abkhác nhau khoảng epsbao nhiêu lần chúng lớn như thế nào, với điều kiện là bất cứ thứ gì có kích thước EPShoặc nhỏ hơn đều được coi là có kích thước EPS. Nếu bạn coi EPSlà giá trị không bình thường nhỏ nhất của loại dấu phẩy động của mình, thì giá trị này rất giống với bộ so sánh của Dawson (ngoại trừ hệ số 2 ^ # bit vì Dawson đo dung sai trong ulps).
Gareth McCaughan

2
Tình cờ, tôi đồng ý với S. Lott rằng Điều đúng sẽ luôn phụ thuộc vào ứng dụng thực tế của bạn, đó là lý do tại sao không có một chức năng thư viện tiêu chuẩn duy nhất cho tất cả các nhu cầu so sánh dấu phẩy động của bạn.
Gareth McCaughan

@ gareth-mccaughan Làm thế nào để xác định "giá trị không bình thường nhỏ nhất của loại dấu phẩy động" của bạn cho python?
Gordon Wrigley

Trang này docs.python.org/tutorial/floatingpoint.html cho biết hầu hết tất cả các cài đặt python đều sử dụng phao chính xác kép của IEEE-754 và trang này en.wikipedia.org/wiki/IEEE_754-1985 cho biết các số được chuẩn hóa gần nhất bằng 0 là ± 2 * * −1022.
Gordon Wrigley

11

Sự khôn ngoan phổ biến rằng các số dấu phẩy động không thể so sánh được cho đẳng thức là không chính xác. Số dấu phẩy động không khác với số nguyên: Nếu bạn đánh giá "a == b", bạn sẽ nhận được đúng nếu chúng là số giống nhau và sai khác (với cách hiểu rằng hai NaN tất nhiên không phải là số giống nhau).

Vấn đề thực tế là đây: Nếu tôi đã thực hiện một số tính toán và không chắc hai số tôi phải so sánh là chính xác, thì sao? Vấn đề này tương tự đối với dấu phẩy động giống như đối với số nguyên. Nếu bạn đánh giá biểu thức số nguyên "7/3 * 3", nó sẽ không so sánh bằng "7 * 3/3".

Vì vậy, giả sử chúng tôi hỏi "Làm thế nào để tôi so sánh các số nguyên cho bình đẳng?" trong tình huống như vậy Không có câu trả lời duy nhất; những gì bạn nên làm phụ thuộc vào tình huống cụ thể, đáng chú ý là loại lỗi nào bạn có và những gì bạn muốn đạt được.

Dưới đây là một số lựa chọn có thể.

Nếu bạn muốn có kết quả "đúng" nếu các số chính xác về mặt toán học sẽ bằng nhau, thì bạn có thể thử sử dụng các thuộc tính của các phép tính bạn thực hiện để chứng minh rằng bạn có cùng một lỗi trong hai số. Nếu điều đó là khả thi và bạn so sánh hai số kết quả từ các biểu thức sẽ cho các số bằng nhau nếu được tính toán chính xác, thì bạn sẽ nhận được "đúng" từ so sánh. Một cách tiếp cận khác là bạn có thể phân tích các thuộc tính của các phép tính và chứng minh rằng lỗi không bao giờ vượt quá một số tiền nhất định, có thể là một số tiền tuyệt đối hoặc một số tiền liên quan đến một trong các đầu vào hoặc một trong các đầu ra. Trong trường hợp đó, bạn có thể hỏi liệu hai số được tính có khác nhau nhiều nhất không và trả về "true" nếu chúng nằm trong khoảng. Nếu bạn không thể chứng minh một lỗi bị ràng buộc, bạn có thể đoán và hy vọng điều tốt nhất. Một cách đoán là đánh giá nhiều mẫu ngẫu nhiên và xem loại phân phối bạn nhận được trong kết quả.

Tất nhiên, vì chúng tôi chỉ đặt yêu cầu là bạn phải "đúng" nếu kết quả chính xác về mặt toán học bằng nhau, chúng tôi bỏ ngỏ khả năng bạn nhận được "đúng" ngay cả khi chúng không bằng nhau. (Trên thực tế, chúng ta có thể đáp ứng yêu cầu bằng cách luôn trả về "true". Điều này làm cho phép tính đơn giản nhưng thường không mong muốn, vì vậy tôi sẽ thảo luận về việc cải thiện tình huống bên dưới.)

Nếu bạn muốn nhận được kết quả "sai" nếu các số chính xác về mặt toán học sẽ không bằng nhau, bạn cần chứng minh rằng đánh giá của bạn về các số mang lại các số khác nhau nếu các số chính xác về mặt toán học sẽ không bằng nhau. Điều này có thể là không thể cho các mục đích thực tế trong nhiều tình huống phổ biến. Vì vậy, hãy để chúng tôi xem xét một sự thay thế.

Một yêu cầu hữu ích có thể là chúng ta nhận được kết quả "sai" nếu các con số chính xác về mặt toán học khác nhau nhiều hơn một số tiền nhất định. Ví dụ, có lẽ chúng ta sẽ tính toán nơi một quả bóng ném trong trò chơi máy tính đi qua, và chúng ta muốn biết liệu nó có đánh một con dơi không. Trong trường hợp này, chúng tôi chắc chắn muốn nhận được "đúng" nếu quả bóng đập vào người dơi và chúng tôi muốn nhận được "sai" nếu quả bóng cách xa con dơi và chúng tôi có thể chấp nhận câu trả lời "không đúng" nếu quả bóng rơi vào một mô phỏng chính xác về mặt toán học đã bỏ lỡ con dơi nhưng chỉ trong một milimet khi đánh con dơi. Trong trường hợp đó, chúng tôi cần chứng minh (hoặc đoán / ước tính) rằng tính toán của chúng tôi về vị trí của quả bóng và vị trí của người dơi có lỗi kết hợp tối đa là một milimet (cho tất cả các vị trí quan tâm). Điều này sẽ cho phép chúng tôi luôn trở về "

Vì vậy, cách bạn quyết định trả lại những gì khi so sánh các số có dấu phẩy động phụ thuộc rất nhiều vào tình huống cụ thể của bạn.

Đối với cách bạn đi về việc chứng minh giới hạn lỗi cho các tính toán, đó có thể là một chủ đề phức tạp. Bất kỳ triển khai điểm nổi nào sử dụng tiêu chuẩn IEEE 754 ở chế độ từ tròn đến gần nhất đều trả về số dấu phẩy động gần nhất với kết quả chính xác cho bất kỳ thao tác cơ bản nào (đáng chú ý là nhân, chia, cộng, trừ, căn bậc hai). (Trong trường hợp hòa, làm tròn nên bit thấp là chẵn.) (Đặc biệt cẩn thận về căn bậc hai và phép chia; việc triển khai ngôn ngữ của bạn có thể sử dụng các phương thức không phù hợp với IEEE 754 cho những điều đó.) Vì yêu cầu này, chúng tôi biết lỗi trong một kết quả duy nhất nhiều nhất bằng 1/2 giá trị của bit có trọng số thấp nhất. (Nếu nhiều hơn, số làm tròn sẽ chuyển sang một số khác trong phạm vi 1/2 giá trị.)

Tiếp tục từ đó trở nên phức tạp hơn nhiều; bước tiếp theo là thực hiện một thao tác trong đó một trong các đầu vào đã có một số lỗi. Đối với các biểu thức đơn giản, các lỗi này có thể được theo dõi thông qua các tính toán để đạt đến một ràng buộc về lỗi cuối cùng. Trong thực tế, điều này chỉ được thực hiện trong một vài tình huống, chẳng hạn như làm việc trên một thư viện toán học chất lượng cao. Và, tất nhiên, bạn cần kiểm soát chính xác đối với chính xác các hoạt động được thực hiện. Các ngôn ngữ cấp cao thường cung cấp cho trình biên dịch rất nhiều sự chậm chạp, do đó bạn có thể không biết các thao tác theo thứ tự nào được thực hiện.

Có nhiều hơn nữa có thể (và) được viết về chủ đề này, nhưng tôi phải dừng lại ở đó. Tóm lại, câu trả lời là: Không có thói quen thư viện cho việc so sánh này bởi vì không có giải pháp duy nhất nào phù hợp với hầu hết các nhu cầu đáng để đưa vào thói quen thư viện. (Nếu so sánh với một khoảng thời gian lỗi tương đối hoặc tuyệt đối đủ cho bạn, bạn có thể làm điều đó một cách đơn giản mà không cần một thói quen thư viện.)


3
Từ cuộc thảo luận ở trên với Gareth McCaughan, so sánh chính xác với một lỗi tương đối về cơ bản lên tới "abs (ab) <= eps max (2 * -1022, abs (a), abs (b))", đó không phải là điều tôi mô tả đơn giản và chắc chắn không phải là thứ mà tôi đã tự mình làm ra. Cũng như Steve Jessop chỉ ra rằng nó có độ phức tạp tương tự như max, min, any và all, tất cả đều là các nội trang. Vì vậy, cung cấp một so sánh lỗi tương đối trong mô-đun toán học tiêu chuẩn có vẻ là một ý tưởng tốt.
Gordon Wrigley

(7/3 * 3 == 7 * 3/3) đánh giá True trong python.
xApple

@xApple: Tôi vừa chạy Python 2.7.2 trên OS X 10.8.3 và đã nhập (7/3*3 == 7*3/3). Nó được in False.
Eric Postpischil

3
Có lẽ bạn đã quên gõ from __future__ import division. Nếu bạn không làm điều đó, không có số dấu phẩy động và so sánh là giữa hai số nguyên.
xApple

3
Đây là một cuộc thảo luận quan trọng, nhưng không phải là vô cùng hữu ích.
Dan Hulme

6

Nếu bạn muốn sử dụng nó trong bối cảnh thử nghiệm / TDD, tôi muốn nói rằng đây là một cách tiêu chuẩn:

from nose.tools import assert_almost_equals

assert_almost_equals(x, y, places=7) #default is 7

5

math.isclose () đã được thêm vào Python 3.5 cho điều đó ( mã nguồn ). Đây là một cổng của nó cho Python 2. Điểm khác biệt của Mark Ransom là nó có thể xử lý "inf" và "-inf" đúng cách.

def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
    '''
    Python 2 implementation of Python 3.5 math.isclose()
    https://hg.python.org/cpython/file/tip/Modules/mathmodule.c#l1993
    '''
    # sanity check on the inputs
    if rel_tol < 0 or abs_tol < 0:
        raise ValueError("tolerances must be non-negative")

    # short circuit exact equality -- needed to catch two infinities of
    # the same sign. And perhaps speeds things up a bit sometimes.
    if a == b:
        return True

    # This catches the case of two infinities of opposite sign, or
    # one infinity and one finite number. Two infinities of opposite
    # sign would otherwise have an infinite relative tolerance.
    # Two infinities of the same sign are caught by the equality check
    # above.
    if math.isinf(a) or math.isinf(b):
        return False

    # now do the regular computation
    # this is essentially the "weak" test from the Boost library
    diff = math.fabs(b - a)
    result = (((diff <= math.fabs(rel_tol * b)) or
               (diff <= math.fabs(rel_tol * a))) or
              (diff <= abs_tol))
    return result

2

Tôi thấy so sánh sau đây hữu ích:

str(f1) == str(f2)

thật thú vị, nhưng không thực tế lắm do str (.1 + .2) == .3
Gordon Wrigley

str (.1 + .2) == str (.3) trả về True
Henrikh Kantuni

Điều này khác với F1 == f2 như thế nào - nếu cả hai đều gần nhưng vẫn khác nhau do độ chính xác, các biểu diễn chuỗi cũng sẽ không bằng nhau.
MrMas

2
.1 + .2 == .3 trả về Sai trong khi str (.1 + .2) == str (.3) trả về True
Kresimir

4
Trong Python 3.7.2, str(.1 + .2) == str(.3)trả về Sai. Phương pháp được mô tả ở trên chỉ hoạt động với Python 2.
Danibix

1

Đối với một số trường hợp bạn có thể ảnh hưởng đến biểu diễn số nguồn, bạn có thể biểu diễn chúng dưới dạng phân số thay vì số float, sử dụng tử số và mẫu số nguyên. Bằng cách đó bạn có thể có sự so sánh chính xác.

Xem Phân số từ mô-đun phân số để biết chi tiết.


1

Tôi thích đề xuất của @Sesquipedal nhưng có sửa đổi (trường hợp sử dụng đặc biệt khi cả hai giá trị đều trả về Sai). Trong trường hợp của tôi, tôi đã dùng Python 2.7 và chỉ sử dụng một hàm đơn giản:

if f1 ==0 and f2 == 0:
    return True
else:
    return abs(f1-f2) < tol*max(abs(f1),abs(f2))

1

Hữu ích cho trường hợp bạn muốn đảm bảo 2 số giống nhau 'chính xác', không cần chỉ định dung sai:

  • Tìm độ chính xác tối thiểu của 2 số

  • Làm tròn cả hai đến độ chính xác tối thiểu và so sánh

def isclose(a,b):                                       
    astr=str(a)                                         
    aprec=len(astr.split('.')[1]) if '.' in astr else 0 
    bstr=str(b)                                         
    bprec=len(bstr.split('.')[1]) if '.' in bstr else 0 
    prec=min(aprec,bprec)                                      
    return round(a,prec)==round(b,prec)                               

Như đã viết, chỉ hoạt động đối với các số không có 'e' trong biểu diễn chuỗi của chúng (có nghĩa là 0.9999999999995e-4 <số <= 0.9999999999995e11)

Thí dụ:

>>> isclose(10.0,10.049)
True
>>> isclose(10.0,10.05)
False

Khái niệm gần gũi không giới hạn sẽ không phục vụ tốt cho bạn. isclose(1.0, 1.1)sản xuất False, và isclose(0.1, 0.000000000001)trả lại True.
kfsone

1

Để so sánh với một số thập phân nhất định mà không có atol/rtol:

def almost_equal(a, b, decimal=6):
    return '{0:.{1}f}'.format(a, decimal) == '{0:.{1}f}'.format(b, decimal)

print(almost_equal(0.0, 0.0001, decimal=5)) # False
print(almost_equal(0.0, 0.0001, decimal=4)) # True 

1

Đây có thể là một chút hack xấu xí, nhưng nó hoạt động khá tốt khi bạn không cần nhiều hơn độ chính xác float mặc định (khoảng 11 số thập phân).

Hàm round_to sử dụng phương thức định dạng từ lớp str dựng sẵn để làm tròn số float thành một chuỗi đại diện cho số float với số thập phân cần thiết, sau đó áp dụng eval cho chuỗi float được làm tròn để lấy lại đến kiểu số float.

Hàm is_close chỉ áp dụng một điều kiện đơn giản cho float nổi lên.

def round_to(float_num, prec):
    return eval("'{:." + str(int(prec)) + "f}'.format(" + str(float_num) + ")")

def is_close(float_a, float_b, prec):
    if round_to(float_a, prec) == round_to(float_b, prec):
        return True
    return False

>>>a = 10.0
10.0
>>>b = 10.0001
10.0001
>>>print is_close(a, b, prec=3)
True
>>>print is_close(a, b, prec=4)
False

Cập nhật:

Theo đề xuất của @stepehjfox, một cách sạch hơn để xây dựng hàm rount_to để tránh "eval" đang sử dụng định dạng lồng nhau :

def round_to(float_num, prec):
    return '{:.{precision}f}'.format(float_num, precision=prec)

Theo cùng một ý tưởng, mã có thể đơn giản hơn nữa bằng cách sử dụng chuỗi f mới tuyệt vời (Python 3.6+):

def round_to(float_num, prec):
    return f'{float_num:.{prec}f}'

Vì vậy, chúng ta thậm chí có thể gói tất cả trong một chức năng 'is_c Đóng' đơn giản và gọn gàng :

def is_close(a, b, prec):
    return f'{a:.{prec}f}' == f'{b:.{prec}f}'

1
Bạn không phải sử dụng eval()để có được định dạng tham số. Một cái gì đó giống như return '{:.{precision}f'.format(float_num, precision=decimal_precision) nên làm điều đó
stephenjfox

1
Nguồn cho nhận xét của tôi và nhiều ví dụ khác: pyformat.info/#param_align
stephenjfox

1
Cảm ơn @stephenjfox tôi không biết về định dạng lồng nhau. Btw, mã mẫu của bạn thiếu dấu ngoặc nhọn kết thúc:return '{:.{precision}}f'.format(float_num, precision=decimal_precision)
Albert Alomar

1
Bắt tốt, và đặc biệt là thực hiện tốt việc tăng cường với chuỗi f. Với cái chết của Python 2 quanh quẩn, có lẽ điều này sẽ trở thành chuẩn mực
stephenjfox

0

Về lỗi tuyệt đối, bạn chỉ cần kiểm tra

if abs(a - b) <= error:
    print("Almost equal")

Một số thông tin về lý do tại sao float hành động kỳ lạ trong Python https://youtu.be/v4HhvoNLILk?t=1129

Bạn cũng có thể sử dụng math.isclose cho các lỗi tương đối

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.