2012-01-15 33 views
5

Mã python sau tính toán số lần lặp lại để thực hiện công cụ dựa trên một số biến. tất cảArithmetics điểm nổi: Có thể không an toàn phụ thuộc vào so sánh cụ thể?

# a - b - c is always a multiple of d. 
    i = (a - b - c)/d 
    while i: 
    # do stuff 
    i -= 1 

Các biến sẽ cùng loại, mà chỉ ints hoặc floats hoặc bất cứ thứ gì. Mối quan tâm của tôi là liệu nó có hoạt động chính xác không nếu các giá trị là floats. Tôi biết đủ để luôn xem xét những cạm bẫy của việc dựa vào các giá trị float chính xác. Nhưng tôi không thể nói nếu điều trên là nguy hiểm hay không. Tôi có thể sử dụng i = int(round((a - b - c)/d)), nhưng tôi tò mò muốn hiểu nổi tốt hơn.

Tất cả đều được giải thích như sau: a - b - c là bội số chính xác của d. Vì vậy, tôi dựa vào (a-b-c)/d để trở thành một giá trị i mà tôi có thể trừ 1 từ và nhận được số lần lặp lại dự kiến ​​trong vòng lặp while, với giả định ngụ ý rằng i == 0 trở thành sự thật. Đó là, có thể tính toán bội số như thế này được giảm đi 1 để đạt được chính xác 0?

Tôi muốn không chỉ biết nếu nó không an toàn, nhưng quan trọng hơn, tôi cần phải hiểu gì về điểm nổi để giải quyết một câu hỏi như thế này? Nếu ai đó biết chắc chắn liệu điều này có an toàn hay không, liệu có thể giải thích như thế không?

+0

Nếu bạn nghĩ 'i' phải có một giá trị số nguyên, hãy đặt nó thành số nguyên. Điều đó sẽ ít nhất đảm bảo rằng vòng lặp của bạn sẽ chấm dứt! – katrielalex

Trả lời

4

Bạn có thể sử dụng decimal module để có được một ý tưởng về những gì "da" giữa một số dấu chấm động như 0.3:

>>> from decimal import Decimal 
>>> Decimal(0.3) 
Decimal('0.299999999999999988897769753748434595763683319091796875') 

Lưu ý rằng Python 2.7 thay đổi cách các số dấu phẩy động được viết (cách hoạt động của repr(f)) để bây giờ hiển thị chuỗi ngắn nhất sẽ cung cấp cùng một số dấu phẩy động nếu bạn thực hiện float(s). Điều này có nghĩa là repr(0.3) == '0.3' bằng Python 2.7, nhưng repr(0.3) == '0.29999999999999999' trong các phiên bản trước đó. Tôi đang đề cập đến điều này vì nó có thể gây nhầm lẫn nhiều thứ hơn nữa khi bạn thực sự muốn xem những gì đằng sau những con số.

Sử dụng các module thập phân, chúng ta có thể thấy lỗi trong tính toán với phao:

>>> (Decimal(2.0) - Decimal(1.1))/Decimal(0.3) - Decimal(3) 
Decimal('-1.85037170771E-16') 

Ở đây chúng ta có thể mong đợi (2.0 - 1.1)/0.3 == 3.0, nhưng có một nhỏ khác không chênh lệch. Tuy nhiên, nếu bạn làm việc tính toán với số dấu chấm động bình thường, sau đó bạn có được không:

>>> (2 - 1.1)/0.3 - 3 
0.0 
>>> bool((2 - 1.1)/0.3 - 3) 
False 

Kết quả được làm tròn ở đâu đó trên đường đi từ 1.85e-16 không phải là zero:

>>> bool(-1.85037170771E-16) 
True 

Tôi không chắc chắn chính xác nơi làm tròn này.

Đối với việc chấm dứt vòng lặp nói chung, sau đó có một đầu mối Tôi có thể cung cấp: for floats less than 253, IEEE 754 can represent all integers:

>>> 2.0**53  
9007199254740992.0 
>>> 2.0**53 + 1 
9007199254740992.0 
>>> 2.0**53 + 2 
9007199254740994.0 

Khoảng cách giữa số biểu diễn là 2 từ 2 -2 , như trình bày ở trên . Nhưng nếu số i của bạn là số nguyên nhỏ hơn 2 thì i - 1 cũng sẽ là số nguyên thể hiện và cuối cùng bạn sẽ đạt được số 0.0, được coi là sai trong Python.

+0

Việc làm tròn xảy ra trong 'Thập phân (2.0) - Thập phân (1.1)', kết quả là 'Thập phân' ('0.8999999999999999111821580300') 'trong khi giá trị' double' chính xác là '0.899999999999999911182158029987476766109466552734375'. Sử dụng '(2.1 - 1.2)/0.3' để tạo ra một giá trị không chính xác ba với' double '. –

3

Tôi sẽ cung cấp cho bạn câu trả lời về ngôn ngữ bất khả tri (Tôi thực sự không biết Python).

Có nhiều sự cố tiềm ẩn trong mã của bạn. Thứ nhất, điều này:

(a - b - c) 

Nếu a là (ví dụ) 10 , và bc đều 1, thì câu trả lời sẽ là 10 , chứ không phải 10 -2 (I 'giả định phao đơn chính xác ở đây).

Sau đó có này:

i = (a - b - c)/d 

Nếu tử số và mẫu số là những con số mà không thể được đại diện chính xác trong dấu chấm động (ví dụ 0,3 và 0,1), thì kết quả có thể không phải là một số nguyên chính xác (nó có thể là 3.0000001 thay vì 3). Do đó, vòng lặp của bạn sẽ không bao giờ chấm dứt.

Sau đó có này:

i -= 1 

Tương tự như trên, nếu i hiện là 10 , sau đó là kết quả của hoạt động này vẫn sẽ là 10 , vì vậy vòng lặp của bạn sẽ không bao giờ chấm dứt.

Do đó, bạn nên cân nhắc thực hiện tất cả các phép tính trong số học số nguyên.

+0

Vâng, điều này cho thấy tính không khả thi của việc sử dụng phao nổi như thế này theo cách đơn giản nhất có thể. Làm cho nó rất rõ ràng, cảm ơn. – porgarmingduod

+0

@MartinGeisler: Vâng, tôi đã thực hiện một cách rõ ràng giả định này (tôi không quen thuộc với các kiểu bản địa Python). Nhưng như bạn nói, tất cả những gì thực sự xảy ra với độ chính xác gấp đôi là độ lớn thay đổi; mọi vấn đề vẫn tồn tại. –

1

Bạn nói đúng rằng có thể có sự không hội tụ về số không (ít nhất là cho nhiều lần lặp hơn bạn dự định). Tại sao không có thử nghiệm của bạn là: while i >= 1. Trong trường hợp đó, như với số nguyên, nếu giá trị i của bạn giảm xuống dưới 1, vòng lặp sẽ kết thúc.

Các vấn đề liên quan