Mục lục Python

Docs / Python

Số trong Python

Game nào cũng đầy phép toán: trừ máu, cộng vàng, tính sát thương chí mạng, chia phần thưởng cho cả đội. Bài này đi qua các phép toán trên int và float trong Python, và một cái bẫy về số thực mà ai cũng dính ít nhất một lần.

Các phép toán cơ bản

Toán tửÝ nghĩaVí dụKết quả
+cộng100 + 25125
-trừ100 - 3070
*nhân12 * 336
/chia10 / 42.5
//chia lấy phần nguyên17 // 53
%chia lấy dư17 % 52
**lũy thừa2 ** 101024
base_damage = 40
crit_multiplier = 2
damage = base_damage * crit_multiplier
print(damage)  # 80

Thứ tự tính giống toán ở trường: ** trước, rồi *, /, //, %, cuối cùng mới +, -. Dùng ngoặc khi muốn tính khác đi.

print(10 + 5 * 2)    # 20
print((10 + 5) * 2)  # 30

Phép chia / luôn ra float

Người học C# hay bất ngờ ở chỗ này. Trong C#, 10 / 2 ra 5 kiểu int. Trong Python, / luôn trả về float:

print(10 / 2)   # 5.0
print(7 / 2)    # 3.5

Khi cần kết quả là số nguyên, ví dụ số món đồ mua được, dùng //.

Chia lấy nguyên // và chia lấy dư %

Có 17 đồng vàng, mỗi bình máu giá 5 đồng. Mua được mấy bình, còn thừa bao nhiêu?

gold = 17
potion_price = 5

potions = gold // potion_price
change = gold % potion_price
print(potions)  # 3
print(change)   # 2

% còn hay dùng để kiểm tra chia hết. Ví dụ cứ mỗi 5 level thì gặp boss:

level = 15
print(level % 5 == 0)  # True, level này có boss

Hay đổi giây ra phút và giây để hiện thời gian chơi:

play_time = 257
minutes = play_time // 60
seconds = play_time % 60
print(minutes, "phút", seconds, "giây")  # 4 phút 17 giây

Lỗi hay gặp: chia cho 0. hp / 0 hay hp // 0 hay hp % 0 đều báo ZeroDivisionError: division by zero. Hay gặp khi tính trung bình điểm mà danh sách người chơi đang rỗng. Kiểm tra số chia khác 0 trước khi chia.

Lũy thừa **

Nhiều game tính exp cần để lên cấp theo lũy thừa:

level = 5
exp_needed = 100 * 2 ** (level - 1)
print(exp_needed)  # 1600

** với số mũ 0.5 là căn bậc hai: 16 ** 0.5 ra 4.0.

Lưu ý: ^ trong Python không phải lũy thừa. 2 ^ 3 ra 1, vì ^ là phép XOR trên bit. Muốn 2 mũ 3 thì viết 2 ** 3.

Làm tròn với round

round(số, số_chữ_thập_phân) làm tròn. Bỏ tham số thứ hai thì làm tròn thành int.

crit_rate = 0.156789
print(round(crit_rate, 2))  # 0.16
print(round(7.8))           # 8

Có một điểm lạ: round làm tròn số ở chính giữa về số chẵn gần nhất.

print(round(2.5))  # 2
print(round(3.5))  # 4

Cách này gọi là làm tròn kiểu ngân hàng, giúp tổng nhiều lần làm tròn không bị lệch về một phía. Nếu game cần luôn làm tròn lên hay xuống, dùng module math:

import math
print(math.ceil(7.1))   # 8, luôn làm tròn lên
print(math.floor(7.9))  # 7, luôn làm tròn xuống

Vì sao 0.1 + 0.2 không bằng 0.3

print(0.1 + 0.2)         # 0.30000000000000004
print(0.1 + 0.2 == 0.3)  # False

Không phải Python tính sai. Máy tính lưu float ở dạng nhị phân, và 0.1 không biểu diễn chính xác được trong hệ nhị phân, giống như 1/3 không viết hết được trong hệ thập phân. C#, JavaScript cũng y hệt.

Hậu quả trong game: so sánh float bằng == có thể sai. Đừng làm vậy. Dùng math.isclose:

import math
speed = 0.1 + 0.2
print(math.isclose(speed, 0.3))  # True

Với tiền tệ trong game, cách an toàn nhất là lưu bằng int. Lưu 150 xu thay vì 1.5 vàng.

Bài tập

Nhân vật có 1000 vàng. Kiếm giá 275. Tính số kiếm mua được, số vàng thừa, và nếu mỗi kiếm được giảm 10% thì giá mới là bao nhiêu (làm tròn 1 chữ số).

Xem đáp án
gold = 1000
sword_price = 275

swords = gold // sword_price
left_gold = gold % sword_price
sale_price = round(sword_price * 0.9, 1)

print(swords)      # 3
print(left_gold)   # 175
print(sale_price)  # 247.5