2012-01-15 16 views
21

Trong chương trình của tôi, độ chính xác thập phân là rất quan trọng.
Rất nhiều phép tính của tôi phải chính xác đến nhiều chữ số thập phân (chẳng hạn như 50).Các vấn đề với số thập phân làm tròn (trăn)

Bởi vì tôi đang sử dụng python, tôi đã sử dụng các mô-đun thập phân (với ngữ cảnh() prec = 99. ví dụ;. Set có 99 chữ số thập phân của độ chính xác khi instantiating một đối tượng thập phân)
như pythonic phao không cho phép bất cứ nơi nào gần độ chính xác như vậy.

Vì tôi muốn Người dùng chỉ định vị trí thập phân của tính chính xác của các phép tính, tôi phải triển khai một số hàm round() trong mã của mình.
Unfortuneately, các chức năng tròn inbuilt và các đối tượng thập phân không tương tác tốt.

round(decimal.Decimal('2.000000000000000000001'),50) 

# Number is 1e-21. There are 21 decimal places, much less than 50. 

Tuy nhiên, kết quả là 2.0 thay vì 2,000000000000000000001
Chức năng vòng không được làm tròn đến 50. ít nhiều!

Có, tôi đã đảm bảo rằng việc làm tròn không xảy ra khi khởi tạo đối tượng thập phân, nhưng sau khi gọi vòng.
Tôi luôn truyền các chuỗi đại diện cho phao cho hàm tạo thập phân, không bao giờ là phao nổi.

Tại sao hàm tròn làm việc này với tôi?
(Tôi nhận ra rằng nó có thể được thiết kế ban đầu cho phao nổi mà không bao giờ có nhiều chữ số thập phân, nhưng tài liệu tuyên bố rằng đối tượng thập phân tích hợp hoàn toàn vào mã python và tương thích với các hàm python sẵn có!)

Cảm ơn rất nhiều!
(Điều này đã cho tôi khá mất bình tĩnh, vì vấn đề này sẽ làm suy yếu việc sử dụng của toàn bộ chương trình)

Specs:
python 2.7.1
Windows 7
mô-đun thập phân (inbuilt)

+0

Ý của bạn là để trừ 2 sau khi làm tròn? – celtschk

+0

Xin lỗi, đó là lỗi đánh máy –

+0

Bạn không biết câu trả lời đúng là gì? Aroth trả lời câu hỏi của tôi, nhưng Raymond đã đưa ra giải pháp! –

Trả lời

7

Vấn đề, như gần như tôi có thể xác định nó, là round() đang trở lại một loại Python float, và không phải loại Decimal. Do đó, bạn không cần phải đặt độ chính xác nào trên mô-đun decimal, bởi vì khi bạn gọi round() bạn không còn có Decimal nữa.

Để giải quyết vấn đề này, có thể bạn sẽ phải tìm ra cách thay thế để làm tròn số không dựa trên round(). Chẳng hạn như lời đề nghị của Raymond.

Bạn có thể tìm thấy ví dụ này ngắn ideone minh họa: http://ideone.com/xgPL9

+0

Ofcourse! Chỉ cần tìm đoạn này từ tài liệu quá ... vòng (a, 1) # round() đầu tiên chuyển đổi thành dấu phẩy động nhị phân –

39

Kể từ round() ép buộc đầu vào của nó vào phao nhị phân thông thường, cách ưu tiên để làm tròn các đối tượng thập phân là với phương pháp quantize():

>>> from decimal import Decimal 
>>> d = Decimal('2.000000000000000000001') 

>>> d.quantize(Decimal(10) ** -20)  # Round to twenty decimal places 
Decimal('2.00000000000000000000') 

Để chặt-off các số không dấu, áp dụng normalize() đến kết quả tròn:

>>> d = Decimal('2.000000000000000000001') 
>>> d.quantize(Decimal(10) ** -20).normalize() 
Decimal('2') 
+0

Tôi đã nhìn vào chức năng này, nhưng nó trả về yuck trailing 0s khi bạn làm tròn đến số thập phân nhiều hơn số thập phân có (các số liệu quan trọng, đó là hợp lý). Các tài liệu cho thập phân nói rằng có hai hoạt động trong quantize; làm tròn và sau đó các số liệu quan trọng (hoặc vì vậy tôi đã giải thích). Có cách nào để thực hiện làm tròn không? –

+0

Sử dụng định dạng chuỗi để kiểm soát sự xuất hiện của số thập phân: '' định dạng (thập phân ('2.00000000000000000000'), '.1f') '' in thành một Bạn cũng có thể chuyển đổi nó thành một chuỗi và sử dụng * rstrip *: '' str (Thập phân ('2.00000000000000000000')). rstrip ('0') ''. –

+0

Dải số 0 với 'Thập phân '(' 2,00000000000000000000 ') .normalize() '. – unutbu

3

Sử dụng round() cho bất kỳ mục đích có khó khăn:

(1) Trong Python 2.7, tròn (Decimal ('39 0,142')) sản xuất 39.14 tức là phao; 3.2 tạo ra một số thập phân.

>>> import decimal; round(decimal.Decimal('2.000000000000000000001'),50) 
2.0 <<<=== ***float*** 

(2) vòng() chỉ có một chế độ làm tròn; Decimal.quantize có nhiều.

(3) Đối với tất cả các kiểu đầu vào, chế độ làm tròn đơn được thay đổi: Python 2.7 vòng 0 (dự kiến ​​trong hầu hết các ứng dụng nghiệp vụ) trong khi Python 3.2 làm tròn tới bội số gần nhất 10 ** - n (ít được mong đợi hơn) kinh doanh).

Sử dụng Decimal.quantize()

4

Hãy thử như sau ...

from decimal import Decimal, ROUND_HALF_UP, getcontext 

getcontext().prec = 51 

def round_as_decimal(num, decimal_places=2): 
    """Round a number to a given precision and return as a Decimal 

    Arguments: 
    :param num: number 
    :type num: int, float, decimal, or str 
    :returns: Rounded Decimal 
    :rtype: decimal.Decimal 
    """ 
    precision = '1.{places}'.format(places='0' * decimal_places) 
    return Decimal(str(num)).quantize(Decimal(precision), rounding=ROUND_HALF_UP) 

round_as_decimal('2.000000000000000000001', decimal_places=50) 

Hy vọng rằng sẽ giúp!

2

Như đã nêu ở trên, vòng() trả về một phao nếu được cung cấp một số thập phân. Đối với các tính toán chính xác, tôi đề nghị để làm cho họ không làm tròn xuống số thập phân, và để làm tròn kết quả cuối cùng, sử dụng một chức năng:

def dround(decimal_number, decimal_places): 
     return decimal_number.quantize(Decimal(10) ** -decimal_places)