Tại sao 1 // 0,01 == 99 trong Python?


31

Tôi tưởng tượng đây là một câu hỏi chính xác về dấu phẩy động cổ điển, nhưng tôi đang cố gắng xoay quanh kết quả này, chạy 1//0.01trong năng suất Python 3.7.5 99.

Tôi tưởng tượng nó là một kết quả mong đợi, nhưng có cách nào để quyết định khi nào an toàn hơn để sử dụng int(1/f)hơn là 1//f?


4
Có, nó luôn an toàn hơn int (1 / f). Đơn giản vì // là bộ phận FLOOR và bạn nghĩ sai về nó là ROUND.
Perdi Estaquel


2
Không trùng lặp. Điều này có thể hoạt động như mong đợi 99,99% bằng cách luôn luôn sử dụng round()và không bao giờ //hoặc int(). Câu hỏi liên quan là về so sánh float không liên quan gì đến việc cắt xén và không có cách khắc phục dễ dàng như vậy.
MAXY

Câu trả lời:


23

Tuy nhiên, nếu đây là phép chia với số thực, 1//0.01sẽ chính xác là 100. Vì chúng là xấp xỉ điểm trôi nổi, tuy nhiên, 0.01lớn hơn một chút so với 1/100, có nghĩa là thương số nhỏ hơn 100. Đó là giá trị 99. đến 99.


3
Điều này không đề cập đến phần "có cách nào để quyết định khi nào an toàn hơn".
Scott Hunter

10
"An toàn hơn" không được xác định rõ.
chepner

1
Đủ để hoàn toàn bỏ qua nó, đặc biệt. khi OP nhận thức được vấn đề dấu phẩy động?
Scott Hunter

3
@chepner Nếu "an toàn hơn" không được xác định rõ, thì có lẽ tốt hơn là yêu cầu làm rõ: /

2
nó khá rõ ràng với tôi rằng có nghĩa là "an toàn hơn" "lỗi không thua kém một máy tính bỏ túi giá rẻ"
MAXY

9

Những lý do cho kết quả này giống như bạn nêu, và được giải thích trong phần toán học dấu phẩy động có bị hỏng không? và nhiều câu hỏi tương tự khác.

Khi bạn biết số thập phân của tử số và mẫu số, một cách đáng tin cậy hơn là nhân các số đó trước để chúng có thể được coi là số nguyên, sau đó thực hiện phép chia số nguyên cho chúng:

Vì vậy, trong trường hợp của bạn 1//0.01nên được chuyển đổi đầu tiên 1*100//(0.01*100)là 100.

Trong trường hợp cực đoan hơn, bạn vẫn có thể nhận được kết quả "bất ngờ". Có thể cần phải thêm một roundcuộc gọi đến tử số và mẫu số trước khi thực hiện phép chia số nguyên:

1 * 100000000000 // round(0.00000000001 * 100000000000)

Nhưng, nếu đây là về làm việc với số thập phân cố định (tiền, xu), thì hãy xem xét làm việc với xu là đơn vị , để tất cả số học có thể được thực hiện dưới dạng số học số nguyên và chỉ chuyển đổi sang / từ đơn vị tiền tệ chính (đô la) khi thực hiện Tôi / O.

Hoặc cách khác, sử dụng thư viện cho số thập phân, như số thập phân , trong đó:

... Cung cấp hỗ trợ cho số học dấu phẩy động thập phân được làm tròn chính xác nhanh.

from decimal import Decimal
cent = Decimal(1) / Decimal(100) # Contrary to floating point, this is exactly 0.01
print (Decimal(1) // cent) # 100

3
"mà rõ ràng là 100." Không nhất thiết: nếu .01 không chính xác, thì .01 * 100 cũng không. Nó phải được "điều chỉnh" bằng tay.
glglgl

8

Điều bạn phải tính đến đó //floortoán tử và do đó trước tiên bạn nên nghĩ rằng nếu bạn có xác suất bằng nhau rơi vào 100 như trong 99 (*) (bởi vì hoạt động sẽ có 100 ± epsilonvới epsilon>0điều kiện là có cơ hội nhận được chính xác 100,00 ..0 là cực kỳ thấp.)

Bạn thực sự có thể thấy điều tương tự với một dấu trừ,

>>> 1//.01
99.0
>>> -1//.01
-100.0

và bạn nên làm (un) ngạc nhiên.

Mặt khác, int(-1/.01)thực hiện đầu tiên phép chia và sau đó áp dụng int()số, không phải là sàn mà là cắt ngắn về 0 ! có nghĩa là trong trường hợp đó,

>>> 1/.01
100.0
>>> -1/.01
-100.0

vì thế,

>>> int(1/.01)
100
>>> int(-1/.01)
-100

Mặc dù làm tròn, sẽ cung cấp cho bạn kết quả mong đợi của BẠN cho toán tử này bởi vì một lần nữa, lỗi là nhỏ đối với những số liệu đó.

(*) Tôi không nói rằng xác suất là như nhau, tôi chỉ nói rằng một tiên nghiệm khi bạn thực hiện một tính toán như vậy với số học trôi nổi là ước tính về những gì bạn đang nhận được.


7

Các số dấu phẩy động không thể biểu thị chính xác hầu hết các số thập phân, vì vậy khi bạn nhập một dấu phẩy động theo nghĩa đen, bạn thực sự có được một xấp xỉ của chữ đó. Số gần đúng có thể lớn hơn hoặc nhỏ hơn số bạn đã nhập.

Bạn có thể thấy giá trị chính xác của số dấu phẩy động bằng cách chuyển nó thành Số thập phân hoặc Phân số.

>>> from decimal import Decimal
>>> Decimal(0.01)
Decimal('0.01000000000000000020816681711721685132943093776702880859375')
>>> from fractions import Fractio
>>> Fraction(0.01)
Fraction(5764607523034235, 576460752303423488) 

Chúng tôi có thể sử dụng loại Phân số để tìm lỗi gây ra bởi nghĩa đen không chính xác của chúng tôi.

>>> float((Fraction(1)/Fraction(0.01)) - 100)
-2.0816681711721685e-15

Chúng ta cũng có thể tìm hiểu làm thế nào các số dấu phẩy động chính xác gấp đôi khoảng 100 bằng cách sử dụng tiếp theo từ numpy.

>>> from numpy import nextafter
>>> nextafter(100,0)-100
-1.4210854715202004e-14

Từ đó, chúng ta có thể phỏng đoán rằng số dấu phẩy động gần nhất 1/0.01000000000000000020816681711721685132943093776702880859375thực tế chính xác là 100.

Sự khác biệt giữa 1//0.01int(1/0.01)là làm tròn. 1 // 0,01 làm tròn kết quả chính xác xuống toàn bộ số tiếp theo trong một bước duy nhất. Vì vậy, chúng tôi nhận được một kết quả 99.

int (1 / 0,01) mặt khác làm tròn theo hai giai đoạn, đầu tiên nó làm tròn kết quả đến số dấu phẩy động chính xác kép gần nhất (chính xác là 100), sau đó làm tròn số dấu phẩy động đó xuống số nguyên tiếp theo (đó là lại chính xác 100).


Gọi đây chỉ là làm tròn là sai lệch. Nó nên được gọi là một trong hai cắt ngắn hoặc làm tròn về phía zero : int(0.9) == 0int(-0.9) == 0
MAXY

Đây là loại dấu phẩy động nhị phân mà bạn đang nói ở đây. (Ngoài ra còn có các loại dấu phẩy động thập phân nữa.)
Stephen C

3

Nếu bạn thực hiện như sau

from decimal import *

num = Decimal(1) / Decimal(0.01)
print(num)

Đầu ra sẽ là:

99.99999999999999791833182883

Đây là cách nó được thể hiện trong nội bộ, vì vậy làm tròn nó xuống //sẽ cho99


2
Nó đủ chính xác để hiển thị lỗi trong trường hợp này, nhưng lưu ý rằng số học "thập phân" cũng không chính xác.
plugwash

Với Decimal(0.01)bạn là quá muộn, lỗi đã len lỏi vào trước khi bạn gọi Decimal. Tôi không chắc đây là câu trả lời cho câu hỏi như thế nào ... Trước tiên, bạn phải tính 0,01 chính xác với Decimal(1) / Decimal(100), như tôi đã trình bày trong câu trả lời của mình.
trincot

@trincot Câu trả lời của tôi là câu hỏi trong tiêu đề "Tại sao 1 // 0,01 == 99" Tôi đã cố gắng chỉ cho OP cách các số trôi nổi được xử lý nội bộ.
Mưa
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.