ماشینحسابی که همیشه راست نمیگوید
پایتون دو نوع عددی اصلی دارد: int (عدد صحیح) و float (عدد اعشاری). int در پایتون اندازهی نامحدود دارد؛ 2 ** 200 بدون سرریز محاسبه میشود. float اما همان استاندارد IEEE 754 است که در همهی زبانها وجود دارد و دقتی حدود ۱۵ تا ۱۷ رقم دارد.
عملگرها
| عملگر | معنی | مثال | نتیجه |
|---|---|---|---|
/ | تقسیم؛ همیشه float | 7 / 2 | 3.5 |
// | تقسیم صحیح (رو به پایین) | 7 // 2 | 3 |
% | باقیمانده | 7 % 2 | 1 |
** | توان | 2 ** 10 | 1024 |
divmod | خارجقسمت و باقیمانده با هم | divmod(125, 60) | (2, 5) |
دقت کنید // رو به پایین گرد میکند، نه رو به صفر: -7 // 2 برابر -4 است. divmod برای تبدیل دقیقه به ساعت و دقیقه یا سانتیمتر به متر و سانتیمتر بسیار کاربردی است.
دام معروف 0.1 + 0.2
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False
>>> import math
>>> math.isclose(0.1 + 0.2, 0.3)
True
این باگ پایتون نیست. کامپیوتر اعداد را در مبنای ۲ ذخیره میکند و 0.1 در مبنای ۲ یک کسر متناوب است، همانطور که 1/3 در مبنای ۱۰ برابر 0.333… است. نتیجه: هیچوقت دو float را با == مقایسه نکنید و هیچوقت پول را با float حساب نکنید.
پول: دو راه درست
راه اول، int به ریال: ریال واحد خرد ندارد، پس همهی مبالغ را بهصورت عدد صحیح ریالی نگه دارید و فقط هنگام نمایش به تومان تبدیل کنید. ساده، سریع و بدون خطای گرد کردن.
راه دوم، Decimal: وقتی درصد، نرخ ارز یا اعشار دارید، از ماژول decimal استفاده کنید که محاسبه را در مبنای ۱۰ و دقیق انجام میدهد:
from decimal import Decimal, ROUND_HALF_UP
price_rial = Decimal("12500000") # قیمت یک تخته فرش
vat = Decimal("0.10") # مالیات بر ارزش افزوده
total = price_rial * (1 + vat)
print(total) # 13750000.00
rate = Decimal("2.675")
print(rate.quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)) # 2.68
toman = int(total) // 10
print(f"{toman:,} تومان") # 1,375,000 تومان
Decimal را همیشه از رشته بسازید. Decimal(0.1) همان خطای float را با خودش میآورد و عددی با ۵۵ رقم اعشار تحویل میدهد.
گرد کردن بانکی
تابع round در پایتون از «گرد کردن بانکی» استفاده میکند: عدد دقیقاً وسط را به نزدیکترین عدد زوج گرد میکند. پس round(2.5) برابر 2 و round(3.5) برابر 4 است. برای فاکتور که انتظار «نیم به بالا» دارد، از quantize با ROUND_HALF_UP استفاده کنید.
نکتههایی که کمتر کسی میداند
round(2.675, 2)برابر 2.67 است نه 2.68؛ چون 2.675 در حافظه در واقع کمی کمتر از 2.675 ذخیره شده است. این یکی گرد کردن بانکی نیست، خطای float است.float("nan") == float("nan")برابر False است؛ NaN تنها مقداری است که با خودش برابر نیست. برای بررسی ازmath.isnanاستفاده کنید.- زیرخط در اعداد (
12_500_000) فقط برای چشم است؛int("12_500")هم کار میکند، ولیint("12,500")خطا میدهد. 10 / 2برابر5.0است نه5؛ اگر نتیجه را برای اندیس لیست یاrangeلازم دارید، از//استفاده کنید.- عدد صحیحی با بیش از ۴۳۰۰ رقم را پایتون بهطور پیشفرض به رشته تبدیل نمیکند (محافظت در برابر حملهی DoS)؛ اگر واقعاً لازم شد،
sys.set_int_max_str_digitsرا تغییر دهید.