İçeriğe geç

Decimal Modülü: Finansal Hesabın 'Tam Hassas' Aracı ve KDV Faciasının Çözümü

Float'la para hesaplamak — yıllar içinde bankaları sallayan klasik bug kaynağı. Python'un `decimal` modülü bu sorunu çözer: tam ondalık precision, kontrollü yuvarlama (ROUND_HALF_UP, ROUND_HALF_EVEN), context yönetimi. Bu derste: TR KDV hesabı, döviz çevirici, e-ticaret sepeti, PostgreSQL NUMERIC entegrasyonu — gerçek production pattern'leri.

Şükrü Yusuf KAYA
22 dakikalık okuma
Orta
Decimal Modülü: Finansal Hesabın 'Tam Hassas' Aracı ve KDV Faciasının Çözümü
💰 Para işiyle uğraşıyorsan bu ders mecburi
Bir e-ticaret sitesi yapıyorsan, bir muhasebe yazılımı kuruyorsan, banka entegrasyonu kodluyorsan — float ile para tutmak disiplin sorunu. Yıllar içinde milyonlarca dolar kaybeden gerçek vakalar var (NASA Ariane 5 roket float overflow ile patladı 1996; bankalar yuvarlama bug'ları ile yıllar boyunca kuruşları kaybetti). Bu ders sana 'how to do it right' anlatıyor — junior dev'in profesyonel olduğu noktalardan biri.

Bir önceki ders'in tekrar — float neden para için kötü?#

>>> 0.1 + 0.2 0.30000000000000004 >>> 19.99 + 0.99 20.98 # Görünüşte sorun yok — ama: >>> total = 0.0 >>> for _ in range(100): ... total += 0.1 >>> print(total) 9.999999999999998 # 10.0 değil
100 küçük tutarın toplamı 10 değil 9.99999...; gerçek dünyada bu kuruş kaybı. Bir bankanın milyonlarca işlem yaptığını düşün — kuruş kuruş birikip büyük rakamlara ulaşır.
Daha kötüsü: yuvarlamada bias. Float
round(0.5)
"bankers rounding" yapıyor (en yakın çift sayıya). Türk vergi kanunu "yarıyı yukarı yuvarla" diyor. Davranışlar uyuşmuyor.
Çözüm:
decimal
modülü.

decimal
modülü — temelleri#

Decimal, IEEE 754-2008 standardında tanımlı decimal floating-point. Yani:
  • Sayıyı decimal tabanda saklıyor (binary değil) — 0.1 sonsuz tekrar etmiyor.
  • Precision'ı sen kontrol ediyorsun (default 28 hane).
  • Rounding mode'u sen seçiyorsun.
from decimal import Decimal, getcontext # String'den oluştur (önerilen) a = Decimal('0.1') b = Decimal('0.2') print(a + b) # 0.3 — TAM! print(a + b == Decimal('0.3')) # True # Precision (varsayılan 28 hane) print(getcontext().prec) # 28 # Bölme — hassas print(Decimal('1') / Decimal('3')) # 0.3333333333333333333333333333 (28 hane) # Float'la karşılaştır print(1/3) # 0.3333333333333333 (~16 hane)
🚨 Çok önemli: Decimal'ı string'den oluştur, float'tan değil!
# 🚫 KÖTÜ Decimal(0.1) # Decimal('0.1000000000000000055511151231257827021181583404541015625') # Float'tan oluşturuyor → float'un saklanan tam değeri Decimal'a geçiyor # ✅ İYİ Decimal('0.1') # Decimal('0.1') # Tam decimal değer # Veya integer'dan Decimal(10) / Decimal(100) # Decimal('0.1')
Bu hata yaygın; her zaman kontrol et: Decimal constructor'ına geçen şey string veya int olsun, asla float olmasın.

Context — precision, rounding, signal'lar#

Decimal'ın davranışı context ile yönetiliyor. Default context:
from decimal import getcontext print(getcontext()) # Context(prec=28, rounding=ROUND_HALF_EVEN, Emin=-999999, Emax=999999, # capitals=1, clamp=0, flags=[], # traps=[InvalidOperation, DivisionByZero, Overflow])
Önemli alanlar:
  • prec: Anlamlı hane sayısı (toplamda — virgülden önce + sonra).
  • rounding: Yuvarlama modu.
  • Emin/Emax: Üs sınırları.
  • traps: Hangi durumlarda exception fırlatılsın.

Precision değiştirme#

from decimal import Decimal, getcontext getcontext().prec = 6 print(Decimal('1') / Decimal('3')) # 0.333333 getcontext().prec = 50 print(Decimal('1') / Decimal('3')) # 0.33333333333333333333333333333333333333333333333333 # Default'a dön getcontext().prec = 28

Rounding modları#

from decimal import Decimal, ROUND_HALF_UP, ROUND_HALF_EVEN, ROUND_HALF_DOWN, \ ROUND_DOWN, ROUND_UP, ROUND_FLOOR, ROUND_CEILING, ROUND_05UP # Test sayısı x = Decimal('2.5') # 8 farklı yuvarlama modes = [ (ROUND_HALF_UP, "Yarı yukarı (klasik)"), # 0.5 → yukarı (ROUND_HALF_DOWN, "Yarı aşağı"), (ROUND_HALF_EVEN, "Yarı çift (bankers)"), # 0.5 → en yakın çift (ROUND_DOWN, "Sıfıra doğru (truncate)"), (ROUND_UP, "Sıfırdan uzağa"), (ROUND_FLOOR, "Sonsuz aşağı"), # negatif sayılar için fark (ROUND_CEILING, "Sonsuz yukarı"), (ROUND_05UP, "Sıfır veya 5 ise yukarı"), ] for mode, desc in modes: r = x.quantize(Decimal('1'), rounding=mode) print(f" {desc:30s} {x} → {r}")
Yarı yukarı (klasik) 2.5 → 3 Yarı aşağı 2.5 → 2 Yarı çift (bankers) 2.5 → 2 (3 çift olmadığı için) Sıfıra doğru (truncate) 2.5 → 2 Sıfırdan uzağa 2.5 → 3 Sonsuz aşağı 2.5 → 2 Sonsuz yukarı 2.5 → 3 Sıfır veya 5 ise yukarı 2.5 → 3
Türkiye finans uygulamaları genelde
ROUND_HALF_UP
(kanunda "yarıyı yukarı yuvarla" geçer). Bankers rounding (default) bias'sız ama beklenmedik.
# Default'u Türkiye için uygun yap getcontext().rounding = ROUND_HALF_UP

Local context (with statement)#

Default'u değiştirmek tehlikeli (global state). Tek bir hesap için local context kullan:
from decimal import localcontext a = Decimal('1') / Decimal('3') # 0.333... (28 hane, default) with localcontext() as ctx: ctx.prec = 6 b = Decimal('1') / Decimal('3') # 0.333333 (6 hane) c = Decimal('1') / Decimal('3') # 0.333... (28 tekrar) — local'dan çıkıldı
Modüler kod yazıyorsan, fonksiyon başında local context aç, kapat — başka kodun hesabını etkilemezsin.

quantize
— sabit ondalık (fixed-point)#

Para hesabı için en kritik metod:
quantize
. Sayıyı belirli bir ondalık haneye yuvarlıyor.
from decimal import Decimal, ROUND_HALF_UP price = Decimal('19.99') vat = Decimal('0.20') raw_total = price * (1 + vat) print(raw_total) # 23.988 (3 ondalık) # 2 ondalığa yuvarla — para format total = raw_total.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP) print(total) # 23.99 # 0 ondalık (tam sayı) whole = raw_total.quantize(Decimal('1'), rounding=ROUND_HALF_UP) print(whole) # 24 # 3 ondalık three_dec = Decimal('19.999').quantize(Decimal('0.001'), rounding=ROUND_HALF_UP) print(three_dec) # 19.999
Decimal('0.01')
"iki ondalık hane istiyorum" demek.
Decimal('0.001')
üç ondalık.
Decimal('1')
tam sayı.
Para işinde her zaman
quantize
ile final değeri yuvarla. Aksi halde 3 ondalıklı para tutarları çıkar — UI'da garip görünür.
# 🚫 Yuvarlama unutulmuş def calculate_total(items): return sum(item.price * item.quantity for item in items) # Sonuç: 23.988 — UI'da "23.988₺" göstermek istemezsin # ✅ def calculate_total(items): raw = sum(item.price * item.quantity for item in items) return raw.quantize(Decimal('0.01'), rounding=ROUND_HALF_UP) # Sonuç: 23.99 — temiz

Gerçek dünya: Türkiye KDV hesabı#

Türkiye'de 4 farklı KDV oranı var (2024+):
  • %1: Temel gıda, kitap (sınırlı liste)
  • %10: İnşaat malzemeleri, bazı hizmetler
  • %20: Genel oran (mal/hizmetlerin çoğu)
  • %0: İhracat, eğitim hizmetleri
Bir e-ticaret platformu yazıyorsun. Müşteri sepete ürün ekliyor, KDV hesabı yap.
from decimal import Decimal, ROUND_HALF_UP, getcontext from dataclasses import dataclass from typing import List # Default'u Türkiye finans için ayarla getcontext().rounding = ROUND_HALF_UP VAT_RATES = { "STANDARD": Decimal('0.20'), # Genel %20 "REDUCED_10": Decimal('0.10'), # %10 "REDUCED_1": Decimal('0.01'), # %1 "ZERO": Decimal('0.00'), # İhracat, eğitim } @dataclass class Product: name: str price: Decimal # KDV dahil mi hariç mi? Modele göre değişir vat_category: str quantity: int = 1 @property def vat_rate(self) -> Decimal: return VAT_RATES[self.vat_category] @property def line_total_excl_vat(self) -> Decimal: """KDV hariç satır toplamı.""" return (self.price * self.quantity).quantize( Decimal('0.01'), rounding=ROUND_HALF_UP ) @property def line_vat(self) -> Decimal: """Bu satırın KDV tutarı.""" return (self.line_total_excl_vat * self.vat_rate).quantize( Decimal('0.01'), rounding=ROUND_HALF_UP ) @property def line_total_incl_vat(self) -> Decimal: """KDV dahil satır toplamı.""" return self.line_total_excl_vat + self.line_vat @dataclass class Cart: items: List[Product] @property def subtotal(self) -> Decimal: return sum((item.line_total_excl_vat for item in self.items), Decimal('0')) @property def total_vat(self) -> Decimal: return sum((item.line_vat for item in self.items), Decimal('0')) @property def total(self) -> Decimal: return self.subtotal + self.total_vat def vat_breakdown(self) -> dict: """Her oran için ayrı toplam.""" breakdown = {} for rate_name, rate in VAT_RATES.items(): items_at_rate = [i for i in self.items if i.vat_category == rate_name] if not items_at_rate: continue base = sum((i.line_total_excl_vat for i in items_at_rate), Decimal('0')) vat = sum((i.line_vat for i in items_at_rate), Decimal('0')) breakdown[rate_name] = { "rate": rate, "base": base, "vat": vat, } return breakdown # Test cart = Cart([ Product("Ekmek", Decimal('5.00'), "REDUCED_1", quantity=2), Product("Süt", Decimal('25.00'), "REDUCED_10", quantity=1), Product("Bilgisayar", Decimal('15000.00'), "STANDARD", quantity=1), Product("Online kurs", Decimal('500.00'), "ZERO", quantity=1), ]) print(f"Subtotal: {cart.subtotal} ₺") print(f"Toplam KDV: {cart.total_vat} ₺") print(f"GENEL TOPLAM: {cart.total} ₺") print("\nKDV oranı kırılımı:") for rate_name, data in cart.vat_breakdown().items(): print(f" %{data['rate']*100:.0f} — Matrah: {data['base']}, KDV: {data['vat']}")
Bu kod production-ready Türk e-ticaret sepet hesabı. Decimal sayesinde:
  • Hatasız (her hesap exact).
  • Yuvarlama net (ROUND_HALF_UP).
  • KDV detayı doğru.
  • Faturada ne göstermen gerektiği belli.
Float'la yapsaydın: bazen 1 kuruş eksik/fazla; muhasebe uyumsuzluğu; gerçek Vergi Dairesi denetiminde gözüne batar.

Gerçek dünya: Döviz çevirici#

USD → TL conversion. Kurun tam değeri var (örn 33.4567).
from decimal import Decimal, ROUND_HALF_UP def convert_currency(amount, exchange_rate, from_decimals=2, to_decimals=2): """ Para çevir. amount: Decimal — kaynak para exchange_rate: Decimal — kur (örn 33.4567 USD/TL) """ raw = amount * exchange_rate return raw.quantize( Decimal('1') / (Decimal('10') ** to_decimals), rounding=ROUND_HALF_UP ) # 100 USD → ?TL (kur 33.4567) usd = Decimal('100.00') rate = Decimal('33.4567') tl = convert_currency(usd, rate) print(f"{usd} USD = {tl} TL") # 100.00 USD = 3345.67 TL # 1234.56 EUR → JPY (JPY ondalıksız genelde, kur 165.43) eur = Decimal('1234.56') eur_jpy = Decimal('165.43') jpy = convert_currency(eur, eur_jpy, to_decimals=0) print(f"{eur} EUR = {jpy} JPY") # 1234.56 EUR = 204253 JPY # Cross-rate (USD → EUR via TL) usd_tl = Decimal('33.4567') eur_tl = Decimal('36.2891') usd_eur = (usd_tl / eur_tl).quantize(Decimal('0.0001'), rounding=ROUND_HALF_UP) print(f"USD/EUR = {usd_eur}") # USD/EUR = 0.9220
Decimal('1') / Decimal('10') ** to_decimals
ile dinamik ondalık hane. JPY 0 ondalık, TL 2 ondalık, BTC 8 ondalık — hepsi çalışıyor.

Önemli: Kur API'sinden gelen veri string olmalı#

import requests # 🚫 Yanlış — float olarak parse data = requests.get("https://api.exchangerate.com").json() rate = Decimal(data["rate"]) # float'tan! precision kaybı # ✅ Doğru — string olarak al rate = Decimal(str(data["rate"])) # str ile float → string → Decimal
İdealde API ham response'unu string olarak döndüre — Stripe API böyle yapıyor (
"amount": "1999"
yerine
"amount": 1999.00
).

Veritabanı entegrasyonu — PostgreSQL NUMERIC#

PostgreSQL'in
NUMERIC(precision, scale)
tipi Decimal ile mükemmel çalışıyor.
-- PostgreSQL şeması CREATE TABLE orders ( id SERIAL PRIMARY KEY, user_id INT NOT NULL, total NUMERIC(12, 2) NOT NULL, -- 10 hane.2 ondalık (max 9_999_999_999.99) vat_amount NUMERIC(12, 2) NOT NULL, exchange_rate NUMERIC(20, 8), -- döviz kuru için 8 ondalık created_at TIMESTAMP DEFAULT NOW() );
import psycopg2 from decimal import Decimal # Default psycopg2 NUMERIC'i Decimal olarak döner conn = psycopg2.connect("dbname=mydb user=me") cur = conn.cursor() # INSERT — Decimal doğal olarak çalışıyor total = Decimal('1234.56') cur.execute( "INSERT INTO orders (user_id, total, vat_amount) VALUES (%s, %s, %s)", (1, total, total * Decimal('0.18')) ) # SELECT — sonuç Decimal cur.execute("SELECT total, vat_amount FROM orders WHERE id = %s", (1,)) row = cur.fetchone() print(type(row[0])) # <class 'decimal.Decimal'> print(row[0] + row[1]) # toplam — exact

SQLAlchemy ile#

from sqlalchemy import Numeric, Column, Integer from sqlalchemy.orm import declarative_base Base = declarative_base() class Order(Base): __tablename__ = "orders" id = Column(Integer, primary_key=True) user_id = Column(Integer) total = Column(Numeric(12, 2)) # Decimal otomatik vat_amount = Column(Numeric(12, 2)) exchange_rate = Column(Numeric(20, 8)) # ORM kullanımı order = Order(user_id=1, total=Decimal('1234.56'), vat_amount=Decimal('222.22')) session.add(order) session.commit() # Dönerken Decimal loaded = session.query(Order).first() print(loaded.total + loaded.vat_amount) # Decimal aritmetik
PostgreSQL NUMERIC + Python Decimal → tam roundtrip. Hesaplama sırasında precision kaybı yok. Para tutan her tabloda NUMERIC kullan, FLOAT/DOUBLE PRECISION kullanma.

JSON serialization — Decimal'ı API'de göndermek#

JSON'da
number
tipi var ama precision belirsiz. Decimal'i JSON'a koymak özel handling ister:
import json from decimal import Decimal data = {"price": Decimal('19.99'), "vat": Decimal('3.998')} # 🚫 Default json.dumps(data) # TypeError: Object of type Decimal is not JSON serializable # ✅ Custom encoder ile string olarak class DecimalEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, Decimal): return str(o) return super().default(o) print(json.dumps(data, cls=DecimalEncoder)) # {"price": "19.99", "vat": "3.998"} # Veya simplejson kütüphanesi (Decimal'i otomatik handle eder) import simplejson print(simplejson.dumps(data, use_decimal=True)) # {"price": 19.99, "vat": 3.998} (number olarak)

En iyi pratik: API response'unda string olarak gönder#

{ "amount": "19.99", "currency": "TRY" }
Stripe, PayPal, modern fintech API'ler bunu yapıyor. Avantaj:
  • JSON parser float'a çevirip precision bozmuyor.
  • Frontend tarafında da
    new Decimal(data.amount)
    ile çalışabiliyorsun (decimal.js).
  • Currency ayrı field — UI biçimleme bağımsız.

FastAPI + Pydantic ile#

from decimal import Decimal from pydantic import BaseModel from fastapi import FastAPI class Order(BaseModel): amount: Decimal currency: str app = FastAPI() @app.post("/orders") def create_order(order: Order): return {"total": order.amount, "currency": order.currency} # Pydantic v2 default'ta Decimal'i string olarak serialize ediyor (3.10+) # {"total": "19.99", "currency": "TRY"}
Modern Python web framework'leri Decimal'i doğru handle ediyor — sen sadece type hint olarak
Decimal
belirtirsin, framework gerisini halleder.

Performans — Decimal ne kadar yavaş?#

Decimal float'tan ~50-100x yavaş. Çünkü software emülasyon (CPU desteği yok).
import timeit from decimal import Decimal # Float t1 = timeit.timeit("0.1 + 0.2", number=1_000_000) print(f"Float: {t1:.3f}s") # ~0.05s (50 ns / op) # Decimal t2 = timeit.timeit( "Decimal('0.1') + Decimal('0.2')", setup="from decimal import Decimal", number=1_000_000 ) print(f"Decimal: {t2:.3f}s") # ~3-5s (3-5 µs / op) # Daha hızlı: Decimal nesneleri pre-create t3 = timeit.timeit( "a + b", setup="from decimal import Decimal; a = Decimal('0.1'); b = Decimal('0.2')", number=1_000_000 ) print(f"Decimal (pre-created): {t3:.3f}s") # ~0.5s
Decimal nesnesi yaratma maliyeti büyük. Eğer döngüde tekrar tekrar aynı sabit kullanıyorsan — bir kez yarat, döngüde kullan.

Pratik: Decimal yavaş ama çoğu uygulama bunu fark etmez#

  • 1000 ürünlü sepet hesabı: ~3 ms (Decimal). Kullanıcıya görünmez.
  • 1 milyon transaction toplam: ~3 saniye. Background job'da ezilir.
  • Real-time HFT: Decimal ASLA kullanılmaz. Tutar 'int * 10000' formunda saklanır (sabit-ölçek integer) veya C/Rust'a geçilir.
Karar: Para görünüşlü iş yapan iş kodunda Decimal. Performance-critical veri pipeline'ında int * scale.
python
# Production-ready e-ticaret sepet helper'ı
# Türkiye için optimize, Decimal ile
 
from decimal import Decimal, ROUND_HALF_UP, getcontext
from dataclasses import dataclass, field
from typing import Optional
 
# Türkiye finans default'u
getcontext().rounding = ROUND_HALF_UP
 
 
def to_money(value, decimals=2) -> Decimal:
"""Herhangi bir sayıyı temiz para Decimal'ına çevir."""
if isinstance(value, Decimal):
d = value
elif isinstance(value, (int, float)):
d = Decimal(str(value)) # float'tan str → Decimal (precision korumalı)
elif isinstance(value, str):
d = Decimal(value.replace(',', '.')) # Türkçe ondalık virgül
else:
raise ValueError(f"Cannot convert {type(value)} to money")
 
quantizer = Decimal('1') / (Decimal('10') ** decimals)
return d.quantize(quantizer, rounding=ROUND_HALF_UP)
 
 
def format_money(value: Decimal, currency: str = "TRY") -> str:
"""Para gösterimi (Türkiye locale: 1.234,56 ₺)."""
symbol = {"TRY": "₺", "USD": "$", "EUR": "€", "GBP": "£"}.get(currency, currency)
# Tausend separator
parts = str(value).split(".")
integer_part = parts[0]
decimal_part = parts[1] if len(parts) > 1 else "00"
 
# Sondan üçer üçer ayır
if integer_part.startswith("-"):
sign, integer_part = "-", integer_part[1:]
else:
sign = ""
 
grouped = ""
for i, c in enumerate(reversed(integer_part)):
if i and i % 3 == 0:
grouped = "." + grouped
grouped = c + grouped
 
return f"{sign}{grouped},{decimal_part} {symbol}"
 
 
# Test
print(to_money("19,99")) # 19.99
print(to_money(19.99)) # 19.99 (str trick)
print(to_money(0.1) + to_money(0.2)) # 0.30 (TAM)
 
print(format_money(Decimal('1234567.89'))) # 1.234.567,89 ₺
print(format_money(Decimal('-19.99'))) # -19,99 ₺
print(format_money(Decimal('100'), "USD")) # 100,00 $
to_money() ve format_money() yardımcıları — bunları projende kütüphane gibi kullan.

Bonus:
Fraction
— kesin rasyonel sayı#

fractions
modülü tam kesir saklıyor — paydası (denominator) ve payı (numerator) ile.
from fractions import Fraction # Kesir oluşturma a = Fraction(1, 3) # 1/3 b = Fraction("1/3") c = Fraction("0.5") d = Fraction(0.5) # float'tan da OK (0.5 binary'de exact) print(a) # 1/3 print(a + a + a) # 1 — TAM (float'ta 0.9999...) # Aritmetik print(Fraction(1, 3) + Fraction(1, 4)) # 7/12 # Float'a dönüş print(float(a)) # 0.3333333333333333 # Decimal'dan Fraction from decimal import Decimal print(Fraction(Decimal('0.1'))) # 1/10

Decimal vs Fraction — ne zaman hangisi?#

DurumDecimalFraction
Para hesabı❌ (kesir gösterimi UI için garip)
Tam rasyonel matematik (örn cebir, geometri)
Yuvarlama kontrolü✅ (modes var)❌ (her zaman tam)
PerformansYavaşDaha yavaş
JSON serializeİyiKötü
Karar: Para → Decimal. Saf matematik (örn 1/3 + 1/4 + 1/6 sonsuz hassas) → Fraction. Çoğu projede Decimal'a ihtiyaç var; Fraction nadir.

Yaygın tuzaklar#

1. Decimal(0.1) yapma#

# 🚫 Decimal(0.1) # 0.1000000000000000055511151231257827021181583404541015625 # float precision sızdı # ✅ Decimal('0.1') # 0.1

2. Float ile karıştırma#

# 🚫 total = Decimal('19.99') * 1.18 # TypeError veya float result total = Decimal('19.99') + 0.5 # decimal.InvalidOperation # ✅ total = Decimal('19.99') * Decimal('1.18') total = Decimal('19.99') + Decimal('0.5')

3. Quantize unutma#

# 🚫 total = sum(item.price * item.qty for item in items) # Sonuç: 1234.56789012 — UI'da garip # ✅ total = sum(...).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)

4. Context global değiştirme#

# 🚫 getcontext().prec = 6 # global! Tüm modülleri etkiler # ✅ with localcontext() as ctx: ctx.prec = 6 # ... hesap

5. Database'de FLOAT kullanma#

-- 🚫 total FLOAT NOT NULL, -- precision kaybı total DOUBLE PRECISION NOT NULL, -- ✅ total NUMERIC(12, 2) NOT NULL, -- exact

6. JSON'da number tip#

// 🚫 {"amount": 19.99} // float parsed; precision kayıp riski // ✅ {"amount": "19.99"} // string; parse with Decimal(s)

7. Decimal'ı sıralama anahtarı yapma#

# OK sorted(items, key=lambda x: x.price) # Tehlikeli (eğer NaN olabiliyorsa) items_with_nan = [Decimal('NaN'), Decimal('1'), Decimal('2')] sorted(items_with_nan) # InvalidOperation hata verebilir
Decimal NaN ile karşılaştırma exception atıyor (float NaN sessizce False dönüyor). Trade-off — Decimal daha sıkı.

Bu derste neler kazandın?#

Float'un finansal hesapta neden yetersiz olduğu (önceki dersten devam).
Decimal modülü — IEEE 754-2008 decimal floating-point.
Decimal('0.1')
vs
Decimal(0.1)
kritik farkı.
Context — prec, rounding mode, traps.
8 rounding mode — ROUND_HALF_UP (Türkiye), ROUND_HALF_EVEN (default bankers), ve diğerleri.
localcontext()
ile fonksiyon-bazlı context yönetimi.
quantize
— fixed-point yuvarlama (para için kritik).
TR KDV hesabı production-ready Cart/Product modelleri.
Currency converter — kur API entegrasyonu.
PostgreSQL NUMERIC + Decimal mükemmel uyum.
JSON serialization — string olarak göndermek.
Performance — Decimal ~50x yavaş ama çoğu uygulamada fark etmez.
Production helper'ları — to_money(), format_money().
Fraction
modülü kısa giriş (saf matematik için).
7 yaygın tuzak — Decimal(0.1), float karıştırma, quantize unutma, vb.
Sıradaki ders:
bool
ve
None
.
True/False
Python'da nasıl çalışır, truthiness kavramı (her şey "doğru veya yanlış" değerlendirilebilir),
None
sentinel'ı,
is None
vs
== None
farkı,
Optional
type hint'i. Sıkça karşına çıkacak küçük ama kritik konular.

Sık Sorulan Sorular

Evet, **gerçekten performance-critical** durumlarda yapılır. Stripe API tutarları cents olarak (`{"amount": 1999}` = $19.99) gönderir. Avantaj: int sınırsız, hızlı. Dezavantaj: her UI gösteriminde /100 yapman gerek, hesaplamada ölçek sürekli akılda — kafa karışıklığı yüksek. **Karar**: Tek-currency, çok hızlı işlem (örn HFT) → int. Multi-currency, business logic ağırlıklı (e-ticaret, muhasebe) → Decimal. Hibrit: DB'de int sakla, business logic'te Decimal'a çevir.

Yorumlar & Soru-Cevap

(0)
Yorum yazmak için giriş yap.
Yorumlar yükleniyor...

İlgili İçerikler