Lý do cho sự khác biệt giữa phân chia số nguyên và float để chuyển đổi int trong python là gì?


52

Gần đây tôi đã nhận thấy rằng làm int()tròn một số float về 0, trong khi phép chia số nguyên làm tròn một số float về phía sàn của nó.

ví dụ:

-7 // 2 = -4
int(-7/2) = -3

Tôi đã đọc tài liệu chỉ định:

lớp int (x, cơ sở = 10)

Trả về một đối tượng số nguyên được xây dựng từ một số hoặc chuỗi x hoặc trả về 0 nếu không có đối số nào được đưa ra. Nếu x là một số, trả về x. int (). Đối với số dấu phẩy động, phần này cắt về không.

và:

phân chia sàn

Phân chia toán học làm tròn xuống số nguyên gần nhất. Toán tử phân chia tầng là //. Ví dụ: biểu thức 11 // 4 ước tính là 2 trái ngược với 2,75 được trả về bởi phép chia float thực. Lưu ý rằng (-11) // 4 là -3 vì đó là -2,75 được làm tròn xuống. Xem PEP 238.

Nhưng có vẻ phi logic đối với tôi rằng 2 phép toán tương tự (phép chia float thành số nguyên) sẽ trả về các kết quả khác nhau.

Có bất kỳ động lực cho sự khác biệt giữa các chức năng?

Cảm ơn bạn.


Câu trả lời:


61

Tính nhất quán.

Bạn sẽ cần phải làm theo một số giải thích rất cơ bản và dường như không liên quan để hiểu nó.

Trong trường bạn đã học phân chia với một phần còn lại. Và bạn đã thực hiện các tính toán như thế này:

8 ÷ 4 = 2 R 0
7 ÷ 4 = 1 R 3
6 ÷ 4 = 1 R 2
5 ÷ 4 = 1 R 1
4 ÷ 4 = 1 R 0
3 ÷ 4 = 0 R 3
2 ÷ 4 = 0 R 2
1 ÷ 4 = 0 R 1
0 ÷ 4 = 0 R 0
        ^------ This is the result of x // 4
            ^-- This is the result of x % 4 (modulo)

Sau đó, bạn đã học được cách chia cho số thực:

8 ÷ 4 = 2.0
7 ÷ 4 = 1.75
6 ÷ 4 = 1.5
5 ÷ 4 = 1.25
4 ÷ 4 = 1.0
3 ÷ 4 = 0.75
2 ÷ 4 = 0.5
1 ÷ 4 = 0.25
0 ÷ 4 = 0.0
        ^--- Note that the number in front of the . is int(x/4)

Cho đến thời điểm này, bạn có thể tin điều đó x // 4int(x/4)luôn cho kết quả tương tự. Đó là sự hiểu biết hiện tại của bạn về tình huống này.

Tuy nhiên, hãy xem điều gì xảy ra trong phép chia số nguyên: số phía sau R chu kỳ từ 3, 2, 1 đến 0 và sau đó khởi động lại: 3, 2, 1, 0. Số ở phía trước R giảm dần sau mỗi bước 4.

Vì vậy, nó sẽ tiếp tục như thế nào?

 8 ÷ 4 =  2 R 0
 7 ÷ 4 =  1 R 3
 6 ÷ 4 =  1 R 2
 5 ÷ 4 =  1 R 1
 4 ÷ 4 =  1 R 0
 3 ÷ 4 =  0 R 3
 2 ÷ 4 =  0 R 2
 1 ÷ 4 =  0 R 1
 0 ÷ 4 =  0 R 0
-1 ÷ 4 = -1 R 3
         ^------ We have to decrease now, because we already have 0 four times
              ^-- We have to restart the cycle at 3

Đồng thời, phép chia số thực cho chúng ta:

-1 ÷ 4 = -0.25
          ^----- There is still a 0 in front of the .

Đó là lý do tại sao -1 // 4cho -1 nhưng int(-1/4)cho 0.

Có bất kỳ động lực cho sự khác biệt giữa các chức năng?

Chà, chúng phục vụ các mục đích khác nhau: //là một phần của phép tính số nguyên với phần dư và int()cung cấp cho bạn phần trước .hoạt động của số thực.

Bạn quyết định những gì bạn muốn tính toán, sau đó bạn quyết định sử dụng toán tử nào trong Python để có kết quả chính xác.

Câu hỏi hay. Tiếp tục học.


11
Trong thực tế, điều này cho phép một mẹo: Nếu bạn có -1 đồ ngọt và bạn tặng nó cho 4 người bạn, thì sẽ có 3 đồ ngọt còn lại. Tuyệt vời phải không? Bạn chỉ cần tìm ra cách sở hữu -1 đồ ngọt.
Thomas Weller

1
Nó tạo ra một số loại nhất quán theo như tôi hiểu động cơ trong việc thêm //toán tử trong python 3 là để tránh buộc phải sử dụng int (float). Nếu đây không phải là trường hợp, khi nào tôi nên chọn thực hiện bằng cách sử dụng int()và khi nào tôi nên thực hiện bằng cách sử dụng//
IsaacDj

1
Ok, đó chỉ là một giả định sai. Điều đó không có gì xấu, miễn là bạn kiểm tra các giả định của mình về tính chính xác, điều này có thể thất bại trong 50% các trường hợp (ít nhất là đối với tôi). Tôi đã thêm một số từ về nó trong câu trả lời.
Thomas Weller

2
@IsaacDj bạn có thể muốn đọc điều này cho câu chuyện đằng sau toán tử "phân chia tầng".
bruno Desthuilliers

1
@EricLippert: Tôi không nghĩ nó kỳ quái. Chúng tôi không thể cho rằng một hoạt động thua lỗ cung cấp kết quả tương tự như cho một hoạt động chính xác. Phát ngôn trong mã: Math.Floor(3.23) != -Math.Floor(-3.23)Vì lý do tương tự -((-x)//y)không cần phải bằng nhau x//y.
Thomas Weller

4

Tôi muốn nói rằng quan sát của bạn rằng 2 thao tác đó phải giống nhau về mặt trực giác được mong đợi vì trên các số dương chúng hành xử giống hệt nhau. Nhưng nếu bạn nhìn vào nguồn gốc của chúng (một cái đến từ toán học và cái kia từ khoa học máy tính) thì nó có ý nghĩa hơn đối với hành vi khác nhau của chúng.

Bạn có thể nhìn vào phía sau các khái niệm:

  • Phân chia tầng hay còn gọi là hàm sàn áp dụng cho phân chia toán học
  • Chuyển đổi loại / Đúc loại

================================================== ================

I) Phân chia tầng hay còn gọi là hàm sàn áp dụng cho phân chia toán học

Hàm sàn là một khái niệm được thiết lập rất tốt trong toán học.

Từ mathworld.wolfram :

Hàm sàn | _ x_ |, còn được gọi là hàm số nguyên hoặc giá trị nguyên lớn nhất (Spanier và Oldham 1987), cho số nguyên lớn nhất nhỏ hơn hoặc bằng x. Tên và biểu tượng cho chức năng sàn được đặt ra bởi KE Iverson (Graham et al. 1994)

Vì vậy, phân chia sàn không có gì khác hơn chức năng sàn áp dụng cho phân chia toán học. Hành vi rất rõ ràng, "chính xác về mặt toán học".

II) Chuyển đổi loại / Đúc loại

Từ wikipedia :

Trong khoa học máy tính, chuyển đổi kiểu, đúc kiểu, ép buộc kiểu và tung hứng kiểu là những cách khác nhau để thay đổi một biểu thức từ kiểu dữ liệu này sang kiểu dữ liệu khác.

Trong hầu hết các ngôn ngữ lập trình, biểu mẫu chuyển thành số nguyên được áp dụng theo quy tắc làm tròn (vì vậy có một quy ước):

  • Làm tròn về 0 - làm tròn theo hướng về 0 (còn được gọi là cắt ngắn)

Quy tắc làm tròn theo tiêu chuẩn IEEE 754 .


Vì vậy, nói cách khác, lý do cho sự khác biệt giữa phép chia số nguyên và float sang chuyển đổi int trong python là một phép toán, đây là một số suy nghĩ từ Guido van Rossum (tôi đoán tôi không phải giới thiệu anh ta: D) (từ blog Lịch sử của Python, bài viết "Tại sao tầng phân chia số nguyên của Python" )

Điều này làm phiền một số người, nhưng có một lý do toán học tốt. Phép toán chia số nguyên (//) và anh chị em của nó, phép toán modulo (%) đi cùng nhau và thỏa mãn một mối quan hệ toán học tốt đẹp (tất cả các biến là số nguyên):

a / b = q với phần còn lại r

như vậy mà

b * q + r = a và 0 <= r <b

(giả sử a và b là> = 0).

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.