Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.
Tìm hiểu Mô-đun decimal của Python
Trong lập trình Python, việc tính toán với số thực nhị phân (float) đôi khi không đảm bảo được độ chính xác tuyệt đối do các giới hạn trong việc biểu diễn số. Để khắc phục vấn đề này, Python cung cấp mô-đun decimal giúp thực hiện các phép toán với số thập phân một cách chính xác và nhanh chóng. Trong bài viết, bạn sẽ tìm hiểu cách sử dụng mô-đun decimal để xử lý các phép toán số thập phân chính xác, cũng như cách điều chỉnh độ chính xác và cơ chế làm tròn của các phép toán này.
Giới thiệu về mô-đun decimal của Python
Nhiều số thập phân không có biểu diễn chính xác trong hệ thống số thực nhị phân, ví dụ như 0.1. Khi sử dụng các số này trong các phép toán, bạn có thể nhận được kết quả không như mong đợi. Ví dụ:
x = 0.1 y = 0.1 z = 0.1 s = x + y + z print(s)
Kết quả là:
0.30000000000000004
Kết quả không phải là 0.3 như mong đợi.
Bài viết này được đăng tại [free tuts .net]
Để giải quyết vấn đề này, bạn có thể sử dụng lớp Decimal từ mô-đun decimal như sau:
import decimal from decimal import Decimal x = Decimal('0.1') y = Decimal('0.1') z = Decimal('0.1') s = x + y + z print(s)
Kết quả là:
0.3
Đầu ra như mong đợi.
Mô-đun decimal của Python hỗ trợ các phép tính toán chính xác tương tự như các phép toán bạn học ở trường.
Không giống như các số thực float, Python đại diện cho các số thập phân một cách chính xác. Và tính chính xác này được giữ nguyên trong các phép toán. Ví dụ, biểu thức sau trả về chính xác 0.0:
Decimal('0.1') + Decimal('0.1') + Decimal('0.1') - Decimal('0.3')
Ngữ cảnh (Context) của Decimal trong Python
Decimal luôn liên kết với một ngữ cảnh kiểm soát các khía cạnh sau:
- Độ chính xác trong một phép toán
- Thuật toán làm tròn
Theo mặc định, ngữ cảnh là toàn cục. Ngữ cảnh toàn cục là ngữ cảnh mặc định. Bạn cũng có thể thiết lập một ngữ cảnh tạm thời có hiệu lực cục bộ mà không ảnh hưởng đến ngữ cảnh toàn cục.
Để lấy ngữ cảnh mặc định, bạn gọi hàm getcontext() từ mô-đun decimal:
decimal.getcontext()
Hàm getcontext() trả về ngữ cảnh mặc định, có thể là toàn cục hoặc cục bộ.
Để tạo một ngữ cảnh mới sao chép từ ngữ cảnh khác, bạn sử dụng hàm localcontext():
decimal.localcontext(ctx=None)
Hàm localcontext() trả về một ngữ cảnh mới sao chép từ ngữ cảnh ctx nếu được chỉ định.
Sau khi có đối tượng ngữ cảnh, bạn có thể truy cập độ chính xác và cách làm tròn qua thuộc tính prec và rounding tương ứng:
- ctx.prec: lấy hoặc đặt độ chính xác. ctx.prec là một số nguyên, mặc định là 28.
- ctx.rounding: lấy hoặc đặt cơ chế làm tròn. Cách làm tròn là một chuỗi, mặc định là 'ROUND_HALF_EVEN'. Lưu ý các số thực float cũng sử dụng cơ chế làm tròn này.
Python cung cấp các cơ chế làm tròn sau:
Làm tròn | Mô tả |
---|---|
ROUND_UP | làm tròn ra xa số không |
ROUND_DOWN | làm tròn về số không |
ROUND_CEILING | làm tròn lên (về phía dương vô cùng) |
ROUND_FLOOR | làm tròn xuống (về phía âm vô cùng) |
ROUND_HALF_UP | làm tròn đến gần nhất, nếu bằng nhau thì làm tròn ra xa số không |
ROUND_HALF_DOWN | làm tròn đến gần nhất, nếu bằng nhau thì làm tròn về số không |
ROUND_HALF_EVEN | làm tròn đến gần nhất, nếu bằng nhau thì làm tròn đến số chẵn |
Ví dụ này minh họa cách lấy độ chính xác và cách làm tròn mặc định của ngữ cảnh:
import decimal ctx = decimal.getcontext() print(ctx.prec) print(ctx.rounding)
Kết quả:
28 ROUND_HALF_EVEN
Ví dụ sau cho thấy cách cơ chế làm tròn 'ROUND_HALF_EVEN' có hiệu lực:
import decimal from decimal import Decimal x = Decimal('2.25') y = Decimal('3.35') print(round(x, 1)) print(round(y, 1))
Kết quả:
2.2 3.4
Nếu bạn thay đổi cách làm tròn thành 'ROUND_HALF_UP', bạn sẽ nhận được kết quả khác:
import decimal from decimal import Decimal ctx = decimal.getcontext() ctx.rounding = decimal.ROUND_HALF_UP x = Decimal('2.25') y = Decimal('3.35') print(round(x, 1)) print(round(y, 1))
Kết quả:
2.3 3.4
Ví dụ sau đây cho thấy cách sao chép ngữ cảnh mặc định và thay đổi cách làm tròn thành 'ROUND_HALF_UP':
import decimal from decimal import Decimal x = Decimal('2.25') y = Decimal('3.35') with decimal.localcontext() as ctx: print('Ngữ cảnh cục bộ:') ctx.rounding = decimal.ROUND_HALF_UP print(round(x, 1)) print(round(y, 1)) print('Ngữ cảnh toàn cục:') print(round(x, 1)) print(round(y, 1))
Kết quả:
Ngữ cảnh cục bộ: 2.3 3.4 Ngữ cảnh toàn cục: 2.2 3.4
Lưu ý rằng ngữ cảnh cục bộ không ảnh hưởng đến ngữ cảnh toàn cục. Sau khối with, Python sử dụng cơ chế làm tròn mặc định.
Hàm tạo (Constructor) Decimal trong Python
Hàm tạo Decimal cho phép bạn tạo một đối tượng Decimal mới dựa trên một giá trị:
Decimal(value='0', context=None)
Tham số value có thể là số nguyên, chuỗi, tuple, số thực float, hoặc đối tượng Decimal khác. Nếu bạn không cung cấp tham số value, nó mặc định là '0'.
Nếu giá trị là một tuple, nó phải có ba thành phần: một dấu hiệu (0 cho số dương hoặc 1 cho số âm), một tuple của các chữ số, và một số mũ:
(sign, (digit1, digit2, digit3,...), exponent)
Ví dụ:
3.14 = 314 x 10^-2
Tuple có ba phần tử như sau:
- sign là 0
- digits là (3,1,4)
- exponent là -2
Do đó, bạn sẽ cần phải truyền tuple sau cho hàm tạo Decimal:
import decimal from decimal import Decimal x = Decimal((0, (3, 1, 4), -2)) print(x)
Kết quả:
3.14
Lưu ý rằng độ chính xác của ngữ cảnh chỉ ảnh hưởng đến phép toán, không ảnh hưởng đến hàm tạo Decimal. Ví dụ:
import decimal from decimal import Decimal decimal.getcontext().prec = 2 pi = Decimal('3.14159') radius = 1 print(pi) area = pi * radius * radius print(area)
Khi bạn sử dụng một số thực float không có biểu diễn chính xác trong hệ nhị phân, hàm tạo Decimal không thể tạo ra một biểu diễn thập phân chính xác. Ví dụ:
import decimal from decimal import Decimal x = Decimal(0.1) print(x)
Kết quả:
0.1000000000000000055511151231257827021181583404541015625
Trong thực tế, bạn sẽ sử dụng chuỗi hoặc tuple để tạo Decimal.
Các phép toán số học Decimal trong Python
Một số toán tử số học không hoạt động giống như số thực float hoặc số nguyên int, chẳng hạn như div (//) và mod (%).
Đối với các số thập phân, toán tử // thực hiện phép chia bị cắt ngắn:
x // y = trunc( x / y)
Lớp Decimal cung cấp một số phép toán học như sqrt và log. Tuy nhiên, nó không có tất cả các hàm được định nghĩa trong mô-đun math.
Khi bạn sử dụng các hàm từ mô-đun math cho các số thập phân, Python sẽ chuyển đổi các đối tượng Decimal thành số thực float trước khi thực hiện các phép toán. Điều này dẫn đến mất độ chính xác đã được tích hợp trong các đối tượng Decimal.
Kết bài
Việc sử dụng mô-đun decimal của Python là cần thiết khi bạn muốn đảm bảo tính toán số thực thập phân chính xác và nhanh chóng. Với lớp Decimal từ mô-đun này, bạn có thể tạo đối tượng Decimal từ nhiều loại giá trị khác nhau như chuỗi, số nguyên và tuple. Đặc biệt, các số thập phân Decimal cho phép bạn kiểm soát độ chính xác và cơ chế làm tròn thông qua ngữ cảnh của chúng. Mặc dù lớp Decimal không cung cấp đầy đủ các phương thức như trong mô-đun math, bạn nên ưu tiên sử dụng các phương thức số học của Decimal để tận dụng độ chính xác và tính toán hiệu quả mà nó mang lại.