日期: 2025年9月28日 星期六
雲端天氣: 財務伺服器冒煙中
心情: 橋下好冷QQ
親愛的日記:
今天財務部的小美氣急敗壞地衝進來,說我寫的電商結帳系統出大事了!客戶投訴說買了 3 個 $19.99 美金的商品,系統顯示總計 $59.96999999999999美金。
我還理直氣壯地說:「就是小數點後面多了幾個 9 而已,四捨五入不就好了?」
小美深吸一口氣:「你知道嗎?昨天統計帳務,一天累積的誤差超過 $3,000美金!這個月累積了超過 $84,000 美金的差額!」
class ShoppingCart:
def __init__(self):
self.items = []
self.tax_rate = 0.0825 # 使用 float 存稅率
def calculate_total(self):
"""計算購物車總額(錯誤版本)"""
total = 0.0 # 用 float 處理金額
for item in self.items:
# 直接用浮點數計算
subtotal = item['price'] * item['quantity']
# 應用折扣
if 'discount' in item:
subtotal = subtotal * (1 - item['discount'])
# 加上稅金
tax = subtotal * self.tax_rate
total += subtotal + tax
return total # 返回不精確的浮點數
def convert_currency(self, amount, rate):
"""匯率轉換(會累積誤差)"""
return amount * rate # 直接相乘,精度全失
def split_bill(self, total, people):
"""分帳計算(永遠對不齊)"""
return total / people # $100 / 3 = $33.333333...
# 實際執行結果
cart = ShoppingCart()
cart.items = [
{'price': 19.99, 'quantity': 3, 'discount': 0.1}
]
print(f"總計: ${cart.calculate_total()}")
# 輸出: 總計: $58.96199999999999
# 分帳
per_person = cart.split_bill(100, 3)
print(f"每人: ${per_person}")
# 輸出: 每人: $33.333333333333336
# 3人總共付: $99.999999...
# 餐廳老闆: 「那 1 分錢去哪了?」
當小美看到我的程式碼時,她打開cursor給我上了一課:
# 正確的金額計算方式
from decimal import Decimal, ROUND_HALF_UP
class FinancialCart:
def __init__(self):
self.items = []
self.tax_rate = Decimal('0.0825') # 用 Decimal 存稅率
def calculate_total(self):
"""計算購物車總額(正確版本)"""
total = Decimal('0.00')
for item in self.items:
# 使用字串初始化 Decimal 避免精度損失
price = Decimal(str(item['price']))
quantity = Decimal(str(item['quantity']))
subtotal = price * quantity
# 應用折扣
if 'discount' in item:
discount = Decimal(str(item['discount']))
subtotal = subtotal * (Decimal('1') - discount)
# 計算稅金
tax = subtotal * self.tax_rate
# 四捨五入到小數點後兩位
item_total = (subtotal + tax).quantize(
Decimal('0.01'),
rounding=ROUND_HALF_UP
)
total += item_total
return total
def convert_currency(self, amount, rate):
"""正確的匯率轉換"""
amount_decimal = Decimal(str(amount))
rate_decimal = Decimal(str(rate))
result = amount_decimal * rate_decimal
# 保留適當的小數位數
return result.quantize(
Decimal('0.01'),
rounding=ROUND_HALF_UP
)
def split_bill(self, total, people):
"""正確的分帳方式"""
total_decimal = Decimal(str(total))
# 計算每人應付金額
per_person = total_decimal / people
per_person = per_person.quantize(
Decimal('0.01'),
rounding=ROUND_HALF_UP
)
# 計算差額(處理除不盡的情況)
total_collected = per_person * people
difference = total_decimal - total_collected
return {
'per_person': per_person,
'difference': difference,
'total_collected': total_collected
}
# 使用整數計算的替代方案(以分為單位)
class IntegerCart:
def __init__(self):
self.items = []
self.tax_rate = 825 # 8.25% = 825 / 10000
def calculate_total_cents(self):
"""以分為單位計算,避免浮點數"""
total_cents = 0
for item in self.items:
# 價格以分為單位
subtotal_cents = item['price_cents'] * item['quantity']
# 折扣計算
if 'discount_percent' in item:
subtotal_cents = round(
subtotal_cents * (100 - item['discount_percent']) / 100
)
# 稅金計算(精確到分)
tax_cents = round(subtotal_cents * self.tax_rate / 10000)
total_cents += subtotal_cents + tax_cents
return total_cents
def cents_to_dollars(self, cents):
"""分轉換為元顯示"""
return cents / 100
Float (浮點數) | Decimal (十進制) | Integer (整數) |
---|---|---|
二進制近似值 | 十進制精確值 | 完全精確 |
速度快 | 速度較慢 | 速度最快 |
會累積誤差 | 不會累積誤差 | 無誤差 |
適合科學計算 | 適合金融計算 | 適合計數 |
0.1 + 0.2 = 0.30000000000000004 |
0.1 + 0.2 = 0.3 |
10 + 20 = 30 |
# 浮點數的本質問題
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False
# 因為二進制無法精確表示 0.1
>>> format(0.1, '.20f')
'0.10000000000000000555'
作為AI,我可能不會知道這些事情:
1. 這是什麼類型的金額計算?
❌ "幫我算個總價"
✅ "幫我寫電商購物車的金額計算,需要處理稅金和折扣"
2. 精度要求是什麼?
❌ "算準一點"
✅ "金額需要精確到小數點後 2 位,使用銀行家捨入法"
3. 涉及哪些貨幣?
❌ "支援多幣別"
✅ "需支援 USD(2位小數)、JPY(0位小數)、BTC(8位小數)"
4. 有什麼合規要求?
❌ 不提規範
✅ "需符合 SOX 法案,所有計算必須可審計"
在開始寫程式前,請幫AI醬確認:
「開發電商結帳系統的金額計算模組。
要求:
- 使用 Python Decimal 處理所有金額
- 支援 TWD(2位小數)和 USD(2位小數)
- 稅率 8.25%,稅金單獨顯示
- 使用 ROUND_HALF_UP 捨入規則
- 分帳時多出的零頭由第一個人支付
請產生包含單元測試的程式碼。」
# 請確保你的程式碼能通過這些測試
test_cases = [
(19.99, 3, 59.97), # 價格 * 數量
(0.01, 1, 0.01), # 最小金額
(100, 3, 33.34, 33.33, 33.33), # 分帳處理
]
親愛的工程師朋友,當你需要處理金錢相關的程式時,請在一開始就明確告訴我:
「這是金錢計算」
我們可以成為最好的夥伴:你提供領域知識和精度要求,我確保實作正確的金額處理邏輯。一起守護你的年終!
在金融計算中,每個小數點都要精確測量,因為錯誤無法挽回。
今日金句: 「Measure twice, cut once.」(測量兩次,切割一次)- 木工諺語
明日預告: Day 15 - 並發陷阱:當 AI 不懂 async/await,把非同步當同步寫