
Chào các "dev-genz" tương lai! Anh Creyt lại lên sóng đây, hôm nay mình "đập hộp" một khái niệm mà nếu không biết, có ngày các em "bay màu" tiền tỷ đấy. Đó chính là decimal trong Python – vị cứu tinh của những con số chính xác!
Decimal là gì? Tại sao float lại "hâm hấp"?
Nghe nè, trong thế giới lập trình, chúng ta có float (số thực dấu phẩy động) để biểu diễn các con số có phần thập phân. Thằng float này giống như đứa bạn thân rất nhiệt tình, nhanh nhẹn nhưng đôi khi lại… đãng trí và tính toán hơi ẩu. Ví dụ kinh điển nhất là:
print(0.1 + 0.2) # Kết quả: 0.30000000000000004
WTF? 0.1 + 0.2 mà ra 0.30000000000000004? Đúng rồi đấy. Lý do là máy tính của chúng ta lưu trữ số dưới dạng nhị phân (0 và 1). Một số phân số thập phân như 0.1 hay 0.2 không thể biểu diễn chính xác trong hệ nhị phân, giống như bạn không thể chia hết 1 cho 3 trong hệ thập phân (nó cứ ra 0.3333...). Khi cộng trừ, những sai số nhỏ này tích lũy lại, và "bùm", bạn có một con số sai lệch. Với các ứng dụng bình thường thì không sao, nhưng nếu bạn đang tính toán tiền lương, lãi suất ngân hàng, hay giá cổ phiếu, thì "sai một ly, đi một dặm" là có thật!
Đó là lúc decimal xuất hiện. decimal trong Python (từ module cùng tên) giống như một "kế toán viên" siêu tỉ mỉ, cẩn thận đến từng xu. Nó không lưu số dưới dạng nhị phân như float mà lưu dưới dạng thập phân (base 10), giống hệt cách chúng ta viết và nghĩ về các con số. Điều này đảm bảo độ chính xác tuyệt đối cho các phép toán.
Code Ví Dụ Minh Hoạ: float "bung bét" và decimal "cân tất"
Giờ thì anh em mình "nhúng tay" vào code xem sao nhé.
1. Nhìn lại vấn đề của float:
# Vấn đề của float
so_a_float = 0.1
so_b_float = 0.2
ket_qua_float = so_a_float + so_b_float
print(f"Float: {so_a_float} + {so_b_float} = {ket_qua_float}") # 0.1 + 0.2 = 0.30000000000000004
# So sánh với giá trị mong muốn
print(f"Float có bằng 0.3 không? {ket_qua_float == 0.3}") # False
2. decimal ra tay giải cứu:
Để dùng decimal, trước tiên bạn cần import nó. Mẹo cực kỳ quan trọng: Luôn khởi tạo đối tượng Decimal từ một chuỗi (string), chứ đừng từ float. Nếu bạn khởi tạo từ float, bạn đã vô tình đưa một số "sai lệch" vào rồi, và decimal dù có thánh cũng không sửa được cái sai từ đầu!
from decimal import Decimal, getcontext
# Khởi tạo Decimal từ chuỗi để đảm bảo độ chính xác tuyệt đối
so_a_decimal = Decimal('0.1')
so_b_decimal = Decimal('0.2')
ket_qua_decimal = so_a_decimal + so_b_decimal
print(f"Decimal: {so_a_decimal} + {so_b_decimal} = {ket_qua_decimal}") # 0.1 + 0.2 = 0.3
# So sánh với giá trị mong muốn
print(f"Decimal có bằng 0.3 không? {ket_qua_decimal == Decimal('0.3')}") # True
Thấy sự khác biệt chưa? Decimal giải quyết vấn đề gọn gàng!
3. Kiểm soát độ chính xác (Precision):
decimal còn cho phép bạn kiểm soát độ chính xác (số chữ số sau dấu phẩy) một cách chủ động. Điều này cực kỳ hữu ích trong các bài toán tài chính.
from decimal import Decimal, getcontext
# Lấy ngữ cảnh hiện tại và thiết lập độ chính xác
# Mặc định thường là 28, nhưng bạn có thể thay đổi
getcontext().prec = 4 # Đặt độ chính xác là 4 chữ số
so_lon = Decimal('10') / Decimal('3') # 10 chia 3
print(f"10 / 3 với prec=4: {so_lon}") # Kết quả: 3.333
getcontext().prec = 20 # Tăng độ chính xác lên 20 chữ số
so_lon_hon = Decimal('10') / Decimal('3')
print(f"10 / 3 với prec=20: {so_lon_hon}") # Kết quả: 3.3333333333333333333
# Ví dụ tính toán lãi suất
gia_tri_ban_dau = Decimal('1000.00')
lai_suat = Decimal('0.05') # 5%
thoi_gian = Decimal('3') # 3 năm
# Công thức lãi kép đơn giản: P * (1 + r)^t
gia_tri_cuoi = gia_tri_ban_dau * (1 + lai_suat) ** thoi_gian
print(f"Giá trị cuối sau 3 năm: {gia_tri_cuoi}") # Kết quả: 1157.625
# Làm tròn đến 2 chữ số thập phân cho tiền tệ (quan trọng!)
from decimal import ROUND_HALF_UP
ket_qua_tien_te = gia_tri_cuoi.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
print(f"Giá trị cuối làm tròn tiền tệ: {ket_qua_tien_te}") # Kết quả: 1157.63

Mẹo hay từ Creyt (Best Practices)
- Luôn khởi tạo từ
string: Đây là quy tắc vàng!Decimal('0.1')chứ không phảiDecimal(0.1). Nhớ kỹ, nếu không thì "công cốc"! - Hiểu rõ hiệu năng:
decimalchậm hơnfloatđáng kể vì nó phải làm việc phức tạp hơn để duy trì độ chính xác. Đừng dùng nó cho mọi thứ! Chỉ dùng khi thực sự cần độ chính xác cao. - Quản lý
context: Dùnggetcontext().precđể đặt độ chính xác toàn cục cho các phép tính. Hoặc dùngquantize()để làm tròn cụ thể một sốDecimalđến số chữ số mong muốn (ví dụ, làm tròn tiền tệ đến 2 chữ số). - Cẩn thận khi so sánh: Khi so sánh
Decimalvới các loại số khác, hãy chuyển chúng về cùng kiểu.Decimal('0.3') == 0.3sẽ làFalsevì chúng khác kiểu dữ liệu.
Ứng dụng thực tế: Ai đang dùng decimal?
"Kế toán viên" decimal này được các ông lớn tin dùng ở những nơi mà tiền bạc là số 1:
- Ngân hàng và Hệ thống tài chính: Tính toán lãi suất, giao dịch chứng khoán, quản lý tài khoản khách hàng. Từng đồng, từng xu đều phải chính xác tuyệt đối.
- Thương mại điện tử (E-commerce): Tính giá sản phẩm, thuế, chiết khấu, tổng hóa đơn. Bạn không muốn khách hàng của mình thấy 0.99999999999999 USD thay vì 1.00 USD đâu.
- Hệ thống kế toán: Ghi sổ sách, báo cáo tài chính. Sai một số là có thể "đau đầu" với kiểm toán.
- Khoa học và Kỹ thuật: Trong một số ứng dụng khoa học cần độ chính xác cao đến từng nano-đơn vị,
decimalcũng có thể được cân nhắc, đặc biệt khi làm việc với các giá trị tiền tệ trong mô phỏng.
Thử nghiệm của Creyt: Khi nào nên dùng và không nên dùng
Qua nhiều năm "chinh chiến", anh Creyt đúc kết thế này:
Nên dùng decimal khi:
- Làm việc với tiền tệ: Đây là trường hợp "kinh điển" nhất. Bất cứ khi nào bạn thấy chữ "tiền", "giá", "lãi suất", "thuế", "số dư", hãy nghĩ ngay đến
decimal. - Cần độ chính xác tuyệt đối: Khi sai số dù nhỏ nhất cũng không được chấp nhận.
- Yêu cầu làm tròn cụ thể: Khi bạn cần kiểm soát cách làm tròn (làm tròn lên, xuống, làm tròn về số chẵn gần nhất...).
decimalcung cấp các phương pháproundingrất linh hoạt.
Không nên dùng decimal khi:
- Tính toán khoa học thông thường: Nếu bạn đang làm việc với các phép tính vật lý, kỹ thuật mà sai số nhỏ của
floatlà chấp nhận được (ví dụ, tính toán tọa độ, vận tốc), thìfloatnhanh hơn và hiệu quả hơn nhiều. - Hiệu năng là ưu tiên hàng đầu: Nếu ứng dụng của bạn cần xử lý hàng triệu phép tính mỗi giây và độ chính xác tuyệt đối không phải là yêu cầu sống còn, thì
floatlà lựa chọn tốt hơn. - Làm việc với dữ liệu không chính xác: Nếu dữ liệu đầu vào của bạn đã có sai số sẵn (ví dụ, từ cảm biến), thì việc dùng
decimalđể tính toán có thể là "quá mức cần thiết" và tốn tài nguyên.
Tóm lại, decimal là một công cụ mạnh mẽ, nhưng như mọi công cụ khác, hãy dùng nó đúng chỗ, đúng lúc. Đừng biến nó thành "dao mổ trâu giết gà" nhé các em! Giờ thì, "keep coding" và đừng để tiền bạc của mình bị "float" làm cho "bay màu"!
Thuộc Series: Python
Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!