2016-11-16 32 views
9

Ai có thể giải thích hoặc đề xuất sửa lỗi cho lý do tại sao khi tôi làm tròn một số thập phân trong Python 3 với bối cảnh được đặt nửa vòng tròn, nó làm tròn 2,5 đến 2, trong khi trong Python 2 nó tròn một cách chính xác đến 3:Python 3 Làm tròn số thập phân một nửa xuống với ngữ cảnh ROUND_HALF_UP

Python 3.4.3 và 3.5.2:

>>> import decimal 
>>> context = decimal.getcontext() 
>>> context.rounding = decimal.ROUND_HALF_UP 
>>> round(decimal.Decimal('2.5')) 
2 
>>> decimal.Decimal('2.5').__round__() 
2 
>>> decimal.Decimal('2.5').quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP) 
Decimal('3') 

Python 2.7.6:

>>> import decimal 
>>> context = decimal.getcontext() 
>>> context.rounding = decimal.ROUND_HALF_UP 
>>> round(decimal.Decimal('2.5')) 
3.0 
>>> decimal.Decimal('2.5').quantize(decimal.Decimal('1'), rounding=decimal.ROUND_HALF_UP) 
Decimal('3') 

Trả lời

9

Chú ý rằng khi bạn gọi round bạn đang nhận được một giá trị float do đó, không phải là Decimal. round là cố định giá trị cho một phao và sau đó làm tròn rằng theo các quy tắc để làm tròn một phao.

Nếu bạn sử dụng tham số ndigits tùy chọn khi bạn gọi round() bạn sẽ nhận được kết quả thập phân và trong trường hợp này nó sẽ làm tròn theo cách bạn mong đợi.

Python 3.4.1 (default, Sep 24 2015, 20:41:10) 
[GCC 4.9.2 20150212 (Red Hat 4.9.2-6)] on linux 
Type "help", "copyright", "credits" or "license" for more information. 
>>> import decimal 
>>> context = decimal.getcontext() 
>>> context.rounding = decimal.ROUND_HALF_UP 
>>> round(decimal.Decimal('2.5'), 0) 
Decimal('3') 

tôi đã không tìm thấy nơi nó được ghi chép lại rằng round(someDecimal) trả về một int nhưng round(someDecimal, ndigits) trả về một số thập phân, nhưng điều đó có vẻ là những gì xảy ra trong Python 3.3 và sau này. Trong Python 2.7, bạn luôn nhận được một float khi bạn gọi round() nhưng Python 3.3 đã cải thiện sự tích hợp của Decimal với các nội trang Python.

Như đã đề cập trong một chú thích, round() đại biểu Decimal.__round__() và đó thực sự thể hiện hành vi tương tự:

>>> Decimal('2.5').__round__() 
2 
>>> Decimal('2.5').__round__(0) 
Decimal('3') 

Tôi lưu ý rằng các tài liệu cho Fraction nói:

__round__() 
__round__(ndigits) 
The first version returns the nearest int to self, rounding half to even. 
The second version rounds self to the nearest multiple of Fraction(1, 10**ndigits) 
(logically, if ndigits is negative), again rounding half toward even. 
This method can also be accessed through the round() function. 

Như vậy hành vi này là phù hợp trong đó không có đối số, nó thay đổi loại kết quả và làm tròn một nửa thành thậm chí, tuy nhiên có vẻ như Decimal không ghi lại hành vi của phương thức __round__ của nó .

Chỉnh sửa để lưu ý như Barry Hurley nói trong phần nhận xét, round() được ghi lại là trả về int nếu được gọi không có đối số tùy chọn và "giá trị dấu phẩy động" nếu có đối số tùy chọn. https://docs.python.org/3/library/functions.html#round

+0

Cảm ơn @Duncan vì đã giải thích, bạn nên cố gắng hiểu những điều này. Có vẻ rất kỳ quặc rằng một _type_ khác được trả về tùy thuộc vào việc bạn có chỉ định ndigits hay không. –

+1

Là một add-on, python 3 tài liệu nói rằng nó đại biểu cho 'số .__ vòng __ (ndigits)' và 'thập phân' thực hiện mô-đun có riêng' __round__' phương pháp –

+1

@ Dunca, nó có vẻ là một int, chứ không phải là float mặc dù: "Nếu ndigits bị bỏ qua hoặc là None, nó trả về số nguyên gần nhất cho đầu vào của nó." https://docs.python.org/3/library/functions.html#round –

2

Mở rộng câu trả lời của @ Duncan, hàm xây dựng round thay đổi giữa python 2 và python 3 thành số chẵn gần nhất (đó là chỉ số trong thống kê).

python2 tài liệu:

... nếu hai bội là như nhau chặt chẽ, làm tròn được thực hiện đi từ 0 (như vậy, ví dụ, tròn (0,5) là 1.0 và tròn (-0,5) là - 1.0).

docs

Python3:

... nếu hai bội là như nhau chặt chẽ, làm tròn được thực hiện về phía thậm chí lựa chọn (như vậy, ví dụ, cả hai vòng (0.5) và tròn (-0,5) là 0 và vòng (1.5) là 2)

Kể từ round chuyển đổi để float nếu không có lập luận được đưa ra cho ndigits (tín dụng để @ câu trả lời của Duncan), round cư xử theo cùng một cách như nó sẽ cho float s.

Các ví dụ (trong python3):

>>> round(2.5) 
2 
>>> round(2.500000001) 
3 
>>> round(3.5) 
4 
+0

Cảm ơn @Billy, nhưng cả hai vòng (1.5) và tròn (2.5) cho 2 trong Python 3, mà tôi đoán là do cách những 1.5 và 2.5 nổi được đại diện nội bộ. vòng trong Python 2 trả về nổi cho những, so với ints int Python 3. –

+2

Nó không có gì để làm với đại diện nội bộ. Triển khai điểm nổi của Python có thể biểu diễn chính xác 1.5 và 2.5 * * (nghĩa là không có lỗi làm tròn dấu phẩy động như bạn sẽ nhận được với số 1.1). Vì 1.5 là * chính xác * bằng khoảng cách từ 1 và 2, python 3 vòng đến số chẵn gần nhất, tương đương 2. Tương tự, 2.5 là * chính xác * giữa 2 và 3, vì vậy nó được làm tròn đến số chẵn gần nhất, lại là 2. – Billy

1

Đây là một sự kết hợp của những thay đổi giữa các chế độ làm tròn của round bằng Python 2 vs 3 và tái thực hiện Decimal từ Python để C (Xem "Khác thay đổi quy mô lớn cuối cùng " trong Features for 3.3 section PEP 398).

Đối với round, chiến lược làm tròn thay đổi như có thể thấy trong What's New In Python 3.0 [Xem thêm Python 3.x rounding behavior]. Thêm vào đó, round bằng Python 3 cố gắng đầu tiên để find an appropriate __round__ phương pháp định nghĩa cho các đối tượng thông qua:

>>> round('1') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: type str doesn't define __round__ method 

Trong khi bằng Python 2.x nó cố gắng đầu tiên để coerce it specifically to a float và sau đó vòng nó:

>>> round('1') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
TypeError: a float is required 

Đối Decimal, bằng Python 2, việc triển khai thậm chí còn thiếu phương thức __round__ để được gọi:

>>> Decimal.__round__ 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
AttributeError: type object 'Decimal' has no attribute '__round__' 

Vì vậy, hãy gọi số round trên đối tượng Decimal ép buộc số này là float có số rounded using _Py_double_round; điều này dẫn đến việc float luôn được trả lại bất kể nếu một giá trị cho ndigits được cung cấp. decimal được triển khai bằng Python thuần túy cho 2.x và (là?) Cho Python 3 cho đến 3.2.

In Python 3.3 it got shinny new __round__ method vì nó đã được tái triển khai tại C:

>>> Decimal.__round__ 
<method '__round__' of 'decimal.Decimal' objects> 

và bây giờ, nó được nhặt bởi round khi round(<Decimal_object>) được gọi.

này, ánh xạ tới PyDec_Round trong C, bây giờ returns a PyLong (một số nguyên) sử dụng bối cảnh mặc định (ROUND_HALF_EVEN) nếu đối số ndigits không được cung cấp, và nếu nó là, gọi quantize vào nó và trả về một đối tượng mới được làm tròn Decimal.

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