Чому 0.1 + 0.2 != 0.3, або як ваш код краде гроші бізнесу
Це продовження першої частини про Multi-tenancy, RLS та Headless Service Layer у SmartHire.
Модулі, багаторівнева ізоляція та сувора Headless-архітектура — це бетонний фундамент. З таким фундаментом можна спати спокійно, знаючи, що дані клієнтів у безпеці, а база витримає будь-який наплив трафіку.
Але навіть найчистіший код не врятує бізнес, якщо він не вміє рахувати гроші.
У цій частині я розкажу про чотири теми:
- Чому я заборонив
floatдля грошей на рівні лінтера - Чому webhook’ам від платіжних систем не можна вірити і як я побудував idempotency
- Які 4 метрики у Grafana дозволяють контролювати здоров’я 1000+ ботів
- Zero-downtime deploy без Kubernetes: blue-green на docker + Caddy
Гроші: чому я заборонив float і перейшов на копійки
Якщо у вашій кодовій базі для грошей використовується float або Decimal без чіткої політики — у вас вже є баг. Ви просто його ще не помітили.
Класичний приклад, який має знати кожен:
>>> 0.1 + 0.2 0.30000000000000004 >>> round(2.5) 2 # banker's rounding в Python 3 — сюрприз для тих, хто чекав 3
У мене float для грошей жив у коді певний час. Коли я почав проєктувати reconciliation з банком — зрозумів: рано чи пізно накопичені rounding errors стануть проблемою. Не чекав поки це станеться на реальних грошах — зробив рефакторинг превентивно. ADR-0014 («Money must be int kopecks») з’явився саме після цього рішення.

У SmartHire діє жорстке правило: гроші — це int у мінімальній одиниці валюти. Для UAH — копійки. Для USD — центи. Усі обчислення всередині — цілочисельна арифметика. Конвертація в Decimal або str відбувається тільки на границях системи: при відображенні користувачу або при відправці у webhook платіжного провайдера.
Для відсотків — basis points (1/10000). Комісія 2.5% записується як commission_bp: int = 250. Множення amount_kopecks * commission_bp // 10000 дає точний результат без жодного float.
Money Pydantic-модель — конвертація в копійки
# money.py:21-74
def to_kopecks(amount: int | float | str | Decimal) -> int:
"""Convert amount to integer kopecks (cents) safely."""
if amount is None:
msg = "Amount cannot be None"
raise MoneyError(msg)
try:
if isinstance(amount, Decimal):
decimal_amount = amount
elif isinstance(amount, str):
decimal_amount = Decimal(amount)
elif isinstance(amount, int | float):
decimal_amount = Decimal(str(amount)) # Float → string first!
kopecks_decimal = decimal_amount * 100
return int(kopecks_decimal.quantize(Decimal("1"), rounding=ROUND_HALF_UP))
except (InvalidOperation, ValueError, TypeError) as e:
msg = f"Failed to convert amount to kopecks: {amount!r} ({e})"
raise MoneyError(msg) from e
Ключовий момент: float перетворюється в str перед Decimal — це єдиний безпечний спосіб уникнути накопиченої похибки IEEE 754.
Hypothesis property-based тест
Ось тест який падає на float і проходить на int:
# test_billing_kopecks_hypothesis.py
@given(a=st.integers(0, 10_000_000), b=st.integers(0, 10_000_000))
def test_add_subtract_invariant(a: int, b: int) -> None:
assert (a + b) - b == a # Float: 0.1 + 0.2 - 0.2 = 0.10000000000000003
@given(kop=kopecks_strategy)
@settings(max_examples=200, suppress_health_check=[HealthCheck.too_slow], deadline=None)
def test_kopecks_round_trip_via_uah(kop: int) -> None:
"""kopecks → to_uah → to_kopecks must return the original value."""
uah = to_uah(kop)
back = to_kopecks(str(uah))
assert back == kop, f"Round-trip failed: {kop} → {uah} → {back}"
Hypothesis генерує 200 випадкових сум і перевіряє інваріант round-trip. Якщо де-небудь є похибка округлення — тест знайде її за секунди.
Заборона float для грошових типів реалізована як кастомний ruff-rule в core/payments/. Якщо напишете amount: float — pre-commit падає з повідомленням "Money must be int kopecks, see ADR-0014".
Платежі: чому webhook’у вірити не можна
Жоден платіжний провайдер у світі — ні Stripe, ні WayForPay, ні LiqPay — не гарантує exactly-once delivery webhook’а. У SLA пишуть «best effort», «at least once», «до 7 спроб з exponential backoff».
Що це означає на практиці? На дистанції в рік на тисячах транзакцій частина webhook’ів не дійде або прийде двічі. Якщо ваш білінг працює виключно на webhook’ах — ви або недоотримуєте гроші, або нараховуєте подвійні доступи.
Три класичні проблеми:
Дубль webhook’а. Провайдер зробив повтор через timeout на своїй стороні — і платіж зараховується двічі.
Пропуск webhook’а. Ваш сервер перезапустився під час deploy’у, webhook прилетів і отримав 502. Клієнт заплатив, доступу нема.
Підробка webhook’а. Зловмисник POST’ить на ваш /webhook довільний JSON з status: "paid" — і отримує преміум-доступ безкоштовно.
У SmartHire всі три проблеми закриті двома механізмами.
HMAC-верифікація + Redis idempotency
# wayforpay_provider.py:121-147
@staticmethod
def verify_callback_signature(
data: dict[str, Any], signature: str, secret: str | None = None
) -> bool:
"""Verify WayForPay callback signature."""
if secret is None:
from core import config
secret = config.WAYFORPAY_MERCHANT_SECRET or ""
try:
expected = WayForPayProvider._compute_hmac(
data, WayForPayProvider._CALLBACK_SIGNATURE_FIELDS, secret
)
return hmac.compare_digest(expected, signature) # Timing-safe!
except (TypeError, ValueError, AttributeError) as e:
logger.error("Callback signature verification error: %s", e)
return False
hmac.compare_digest() — обов’язково. Звичайне == вразливе до timing attack: за різницею у часі відповіді можна підібрати підпис побайтово.
# billing_webhook.py:118-143
# Idempotency через Redis:
async with redis_entity_lock("webhook", order_id, ttl_seconds=60):
claim = await service.claim_webhook_idempotency(
provider="wayforpay",
order_id=order_id,
event_type=event_type,
tenant_id=tenant_id,
request_hash=request_hash,
)
if claim is None:
# Already processed — idempotent duplicate
return web.json_response({"status": "ok", "message": "Already processed"})
Логіка проста: перед обробкою перевіряємо чи вже бачили цей order_id. Якщо так — повертаємо 200 OK без повторної обробки. TTL 60 секунд достатньо для захисту від retry-storm.
Reconciliation як страховка
Webhook — це тільки половина рішення. Друга половина — щоденна reconciliation.
О 9:00 ранку стартує APScheduler-job, який тягне з API WayForPay/LiqPay усі транзакції за минулу добу, порівнює з нашою БД і генерує Excel-звіт з трьома листами:
- Paid — все ОК
- Pending — платіж у банку є, у нас нема (webhook пропущено)
- Розбіжності — суми не сходяться або дублікат
Звіт прилітає оператору в Telegram (див. нижче). Якщо листи Pending і розбіжності пусті — день минув добре.

BillingLifecycle FSM
Стан тенанта — це не функція від webhook’а. Це окремий FSM зі станами та timeout-переходами через APScheduler:
TRIAL → ACTIVE (activate()) ACTIVE → GRACE_PERIOD (payment_fail()) GRACE_PERIOD → PAUSED (grace_expired()) PAUSED → ACTIVE (resume())
Якщо платіж не прийшов до дедлайну — переходимо в GRACE_PERIOD (3 дні), потім PAUSED. Webhook — лише один з тригерів переходу, не єдиний.

Observability: 4 метрики замість двохсот
«Ми додали Prometheus» не дорівнює observability. Питання не в кількості метрик, а в тому, скільки з них ви відкривали минулого тижня. Якщо у вас 200 dashboard’ів і всі вони — graveyard з останнім оновленням півроку тому, у вас немає observability.

У SmartHire є чотири метрики, на які я дивлюсь щоранку:
smarthire_active_tenants — Gauge, кількість тенантів з активністю за останні 24 години. Падіння на цього показника за добу — привід відкрити Sentry.
smarthire_bot_messages_total{tenant_id, module_name} — Counter повідомлень з розбивкою по тенантах і модулях. Якщо один тенант видає 10x навантаження — або у нього успіх, або запустився bot-loop.
smarthire_payments_total{status} — Counter платежів. Якщо status="failed" росте швидше за status="success" — ламається інтеграція з провайдером.
smarthire_db_query_duration_seconds{query_type} — Histogram з OLTP-бакетами. p99 повзе вгору — десь з’явився N+1 або забутий індекс.
Prometheus екпортер з tenant labels
# metrics_exporter.py:70-120 ACTIVE_TENANTS = Gauge( "smarthire_active_tenants", "Number of currently active tenants", ) BOT_MESSAGES_TOTAL = Counter( "smarthire_bot_messages_total", "Total number of bot messages, per tenant and module", ["tenant_id", "module_name"], # ← tenant-aware labels ) PAYMENTS_TOTAL = Counter( "smarthire_payments_total", "Total number of payment transactions", ["status"], ) DB_QUERY_DURATION = Histogram( "smarthire_db_query_duration_seconds", "Duration of database queries in seconds", buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0), ) # Використання: metrics.inc_bot_messages(tenant_id="acme_corp", module_name="ankety") metrics.inc_payments(status="success")
Tenant-aware labels — ключова деталь. Без них при інциденті незрозуміло: «це у всіх погано чи в одного клієнта?» З ними — за 30 секунд бачиш що проблема в конкретному тенанті.
Окремо про алерти. Поганий алерт — CPU > 80%. Спрацьовує на третій секунді деплою при rolling restart, всі його ігнорують. Хороший алерт — p99 latency > 500ms протягом 5 хвилин AND error rate > 1%. Спрацьовує тільки при справжніх проблемах.
OpenTelemetry traces летять у Tempo, trace_id інжектиться в structlog — у Logtail можна знайти всі логи одного запиту через всю систему. Sentry — тільки для unhandled exceptions, не для warning’ів. Шум вбиває сигнал.
Zero-downtime deploy: blue-green без Kubernetes
Якщо ваш deploy = downtime — у вас немає SaaS. Multi-tenant система мусить оновлюватись без переривання сесій.
Класичний flow, який я переріс: git pull && systemctl restart bot — 30 секунд downtime, користувачі бачать «бот не відповідає».
Зараз це працює через blue-green deploy на рівні docker + Caddy:
- Збираємо і запускаємо новий контейнер (
api-blueабоapi-green) - Чекаємо readiness probe (
/readiness) — поки всі залежності не зелені, трафік не переключаємо - Чекаємо поки Caddy визначить healthy upstream
- Переключаємо трафік на новий контейнер
- Gracefully зупиняємо старий з
stop_grace_period=60s

Kubernetes Helm chart з HPA — наступний крок масштабування, не поточна реальність. Але blue-green на docker вже дає zero-downtime для поточного навантаження.
Graceful shutdown handler
# shutdown.py:46-112
async def _wait_for_tasks(self) -> None:
"""Wait for active tasks or cancel after 30s timeout."""
current_task = asyncio.current_task()
tasks = [t for t in asyncio.all_tasks() if t is not current_task and not t.done()]
if not tasks:
return
logger.info("Waiting for %s active tasks (max 30s)...", len(tasks))
_, pending = await asyncio.wait(tasks, timeout=30.0)
if pending:
logger.warning("%s tasks did not complete. Forcing cancel.", len(pending))
for task in pending:
task.cancel()
async def shutdown(self, sig: signal.Signals | None = None) -> None:
"""Graceful shutdown sequence."""
if self.is_shutting_down:
return
self.is_shutting_down = True
logger.warning("Received %s. Starting Graceful Shutdown...", sig.name if sig else "Shutdown")
await self._wait_for_tasks() # 1. Drain in-flight requests
await self._close_postgres() # 2. Close DB pool
await self._close_redis() # 3. Close Redis
logger.info("Graceful Shutdown complete.")
SIGTERM прилітає — маємо 30 секунд на drain: зупиняємо прийом нових запитів, чекаємо завершення активних, закриваємо пули БД і Redis. Якщо за 30 секунд не встигли — SIGKILL. Тому всі довгі операції (reconciliation, batch jobs) запускаються в окремих контейнерах через CronJob, а не у головному боті.
Що я зробив би інакше: чесний розділ про граблі
Стаття без визнання помилок — маркетинговий буклет.
SQLite на старті. Класична пастка — «спочатку MVP, потім мігрую». Міграція на PostgreSQL зайняла більше місяця. Якби починав зараз — Postgres з нульового дня, навіть якщо здається overkill. Невелика економія на старті коштувала місяця роботи.
RLS додав не одразу. Перший час жив на «я ж завжди пишу WHERE tenant_id». Потім зрозумів що покладатись на власну дисципліну — антипатерн. RLS — це не «оптимізація на потім», це базова гігієна з першої міграції.
Float для грошей. Зробив рефакторинг превентивно, не чекаючи реальних розбіжностей у reconciliation. Але якби почав з class Money з першого дня — заощадив би час на рефакторинг.
Idempotency для webhook’ів. Додав на етапі проєктування платіжного модуля, не після інциденту. Але якщо ви ще не додали — додайте зараз, не чекайте першого дублю.
OTEL додав не одразу. Якби починав зараз — OpenTelemetry з першого тижня, навіть якщо здається, що проєкт ще маленький.
Висновок: п’ять речей, які забрати з собою
Якщо немає часу на весь текст вище — ось summary:
Float для грошей — баг за визначенням. int у копійках, basis points для відсотків, Decimal тільки на границях системи. Перевіряється кастомним lint-rule.
Webhook ≠ source of truth. Жоден провайдер не гарантує exactly-once delivery. HMAC + idempotency на вході, щоденна reconciliation з API банку як страховка, окремий FSM для стану білінгу — три механізми які закривають всі сценарії.
Observability — це 4 метрики на які реально дивишся, а не 200 dashboard’ів яких ніхто не відкривав. Tenant-aware labels обов’язково — інакше при інциденті незрозуміло де шукати.
Zero-downtime можливий без Kubernetes. Blue-green на docker + Caddy + readiness probe вже дає deploy без переривання сесій.
Починайте з вибору правильних інструментів для масштабу задачі. Postgres замість SQLite, RLS з першої міграції, Money як int з першого рядка — це не overkill, це інвестиція яка окупається через місяць.
Якщо є досвід з фінансовою інженерією в Python або альтернативні підходи до idempotency — буду вдячний за коментарі.
Репозиторій SmartHire — приватний, але архітектурні рішення та showcase доступні на GitHub.
Підписуйтесь на LinkedIn — там анонсую наступні частини.
36 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарівen.wikipedia.org/...wiki/Non-decimal_currency
С учетом этого, вариант с int почти хорош конечно.
Посмотрел у себя в системе SDK платежек:
— PayPal на основе строк в JSON
— Square и Stripe на основе int
Дякую за крутий фідбек та реальні приклади з платіжних систем! 🙂
Абсолютно в кожному проєкті і компанії де я працював доводилось виправляти «баг з копійкою» через те, що розробник подумав що тип float чи int мають достатню точність для розрахунків з грошами.
Ось саме тому я і написав цю статтю 🙂 Якщо хоча б один розробник після прочитання не допустить цей баг — вже варто було.
А що децімал в пітоні не вміє в складання дробних чисел? Чому це проблема? В цілому це тип як раз створювався для грошей та цієї банальної проблеми коли 0.1+0.2 не дають 0.3.
Decimal — це класичний і правильний вибір для більшості проєктів. Але у великих SaaS-системах з купою інтеграцій у нього з’являються свої болючі нюанси.
По-перше, це серіалізація в API. JSON не знає, що таке Decimal. Довелося б або віддавати його як рядок (і змушувати TypeScript-фронтенд парсити його назад), або конвертувати у float для мережі (що перекреслює всю точність). Ціле число в копійках проходить крізь будь-які API, мобільні додатки та вебхуки без жодного викривлення типів.
По-друге, це база даних. У Postgres BIGINT зберігається, індексується і працює швидше, ніж довільний NUMERIC чи DECIMAL під великим навантаженням.
Ну і нативна арифметика з int у Python все одно швидша та легша за Decimal. Тому int в базі — це вибір на користь простоти інтеграцій та швидкості роботи БД!
Дякую зрозумів, так фронт та база дійсно валідні поінти. Хоча стосовно перформансу навіть хз чи дійсно воно того варто, єнівей то вже по кожному проекту різне буде звісно.
Ключова думка автора в тому щоб власне використовувати Decimal замість float
Та мене ось це трохи сконфузило
Decimal, як мінімум у Python має фіксовану довжину, але її варто явно фіксувати. Аби цифри десь по дорозі не загубились з різними розмірностями. Тому потрібна «чітка політика»
еще есть нюанс валютами вроде йены, например, где нет копеек. инт с «минимальной денежной единицей» такую проблему решает
В цьому підході є мінус.
Нехай у вас замовлення 10 різних товарів, і ви рахуєте для кожного податок чи акциз.
Тоді на кожній позиції у вас переплата (або недоплата, що ще гірше) до 0.5 цента. В сумі це вже 5 центів.
Я використовував BigDecimal, жодних проблем
Ви праві, це класична проблема попозиційного заокруглення.
У фінтеху та е-комерс її вирішують двома стандартними шляхами:
1) Розрахунок від загальної суми: податок або акциз нараховується не на кожен товар окремо, а на сумарну вартість усього кошика, і заокруглюється лише один раз наприкінці. Це зводить максимальну похибку до 0.5 копійки на все замовлення.
2) Внутрішня висока точність: для проміжних розрахунків у пам’яті (податки, знижки, комісії) ми використовуємо базисні пункти (basis points, 1/10000) або умовні «мікрокопійки», а фінальне заокруглення до цілих копійок робимо вже один раз — при записі фінального інвойсу в базу та відправці на платіжний шлюз.
Саме тому в статті я зазначив, що висока точність використовується як проміжне значення в пам’яті під час обчислень, але фінальний результат у БД завжди фіксується як ціле число (int).
Дякую за чудове питання. Поправте мене, якщо я десь помиляюсь.
О то ви ще не робили податкову накладну для роздрібної торгівлі. В роздрібній торгівлі ціна з ПДВ а в податковій накладній ціна без пдв і пдв на всю накладну. Єдине що спасає ціна має бути кратна 6 копійкам. Все інше до одного місця
Дуже цікавий коментар, я справді ще не мав досвіду з цим, тож цікаво було про це дізнатися. Дякую! 🙂
Тримати все цілим — це чудовий спосіб. Він дуже простий, а простота це найголовніше. Якби це був мій пет-проект, я би взяв Decimal, але з точки зору технічного управління проектами справді краще взяти int.
У статті так і не прозвучало, чому 0,1 + 0,2 ≠ 0,3. Відповідь вірменського радіо: звичайно, 0,1 + 0,2 = 0,3, але у IEEE754-флоатах неможливо точно виразити ані 0,1, ані 0,2, ані 0,3.
Дякую за коментар!
Повністю згоден з вами щодо простоти int. Коли система масштабується і доводиться передавати гроші через API, черги повідомлень та різні фронтенди, серіалізувати звичайні цілі числа в JSON набагато безпечніше та простіше, ніж тягнути Decimal з його специфікою через усі інтеграційні шари.
Щодо IEEE 754 — ви абсолютно праві. Я свідомо не хотів перевантажувати статтю лонгридом про теорію двійкового кодування дробів та нескінченні періодичні дроби, але ви сформулювали це максимально лаконічно та влучно. Дякую за чудове доповнення до матеріалу!
САБ Казначейства — в копійках. Проте, в частині платежів в бюджети, це добре для акумуляції (додавання/накопичення), а коли розмежування (кому скільки наділити за кодом доходу із зібраного у відсотках), то — приведення розрахункових значень до кількості неподільних одиниць і можливо віднесення залишкової одиниці на одного з.
Те, що САБ Казначейства працює в копійках — це найкращий доказ для всієї статті 🤝
Ви порушили дуже важливе питання щодо розподілу залишкових копійок. Дійсно, якщо ми ділимо умовну 101 копійку порівну, виникає оця «залишкова» копійка, яку неможливо розділити фізично.
Я вирішую це досить просто: округлюю частки до найближчої копійки донизу (floor), а весь залишок (зазвичай 1 копійку) за логікою бізнесу примусово докидаю на головний рахунок отримувача.
Це вимагає додаткових перевірок у коді, але повністю убезпечує від фінансових дірок, які обов’язково створив би float.
Дякую за такий крутий коментар і за те, що підсвітили цю тему!
використовуєте бігінт (якщо суми очікувані оперецій десь в межах держборгу США на 2026р.) і рахуєте в шагах (копійках, центах, сатошах, лампортів чи чому там найменшому неділимому велью)
Саме так! У базі копійки зберігаються якраз у BIGINT — про всяк випадок, якщо обороти SmartHire долетять до масштабів держборгу США 🙂
Мені довелося погуглити, що таке «лампорти», але логіка на 100% правильна: рахувати все в мінімальних неподільних одиницях — це єдиний надійний шлях для будь-якої системи.
Дякую за підтримку та цікаве доповнення!
Тому що:
Odesa I.I.Mechnikov National University
Master’s degree, International relations
Якби у вас була профільна освіта, то вам би розповіли про те як працює плаваюча точка в першому семестрі на першому курсі. І ви б не писали це в статті для ІТ спільноти.
Далі вже цікавіше:
В деяких випадках — зазвичай там де треба швидкість обчислень, доводиться використовувати числа з плаваючою точку. Але тоді треба для порівняння використовувати різницю менше епсілон, різні стратегію округлення (є та яка називається «банківська»).
Але це вже матеріал не першого семестру першого курсу
Мій бакалаврат — «Фінанси та банківська справа», якщо цікаво 🙂
Щодо float з епсилонами — це валідний підхід для наукових обчислень чи геймдеву.
Але жоден банк, Stripe чи будь-яка платіжна система не зберігає транзакції у float. Тільки цілі числа. Це не думка — це галузевий стандарт.
Про банківське округлення (ROUND_HALF_EVEN), до речі, я якраз і написав у статті — ми використовуємо його для розрахунку відсотків.
2 з найбільших банків в штатах оперують флоатами (принаймні в деяких підсистемах). Зберігають, те що я бачив, в строках. Для передачі по мережі використовують флоат.
Пару бірж так само.
Умовний бігдецімал дуже розповсюджений. Знову ж по причині, того, що флоат мають певні проблеми, що вивчають на вступному курсі в універі.
Для чого про це писати статтю на типу професійному ресурі не зрозуміло.
Використання float у системах деяких банків США — це не архітектурний орієнтир, а технічний борг, з яким вони борються роками. Сучасні платіжні гіганти (Stripe, Shopify та інші) у своїх API працюють виключно з integer у мінімальній одиниці валюти. У міжнародних стандартах транзакцій (ISO 8583) типу float немає взагалі.
Писати про це на професійному ресурсі варто саме тому, що навіть досвідчені інженери іноді намагаються виправдати плаваючу точку у фінансових розрахунках.
Ще раз:
І ви б це знали, якби заглибились в тему.
Ви наводите формат передачі/кодування даних, як аргумент проти використання певного типу для обчислень. Ви ж не передаєте 0.2+0.3?
В тому ж фіксі Qty довго був інтом. А потім щось сталось і тепер «Same as float» www.fixtrading.org/...tandards/tagvalue-online
Тут знову ж важливий момент:
На етапі передачі даних там скоріше за все фіксед поінт строка. При роботі в пам’яті більшість бібліотек декодують значення у флоат, бо з ним швидше працювати. Якщо вам треба швидко працювати.
На спеціальності «Фінанси та банківська справа» вам пояснювали різницю виділення змінних на стеку та на кучі (в хіпі)?
У CPython немає поняття «числа на стеку» — усі змінні (і int, і float) під капотом є об’єктами (PyObject) і алокуються виключно на купі (heap). Тому в контексті Python-розробки (про яку стаття) цей аргумент не зовсім релевантний.
Щодо бібліотек, які «декодують фінансові значення у float в пам’яті для швидкості» — у фінтех-системах це зазвичай вважається критичним архітектурним дефектом. У пам’яті для грошей використовують або decimal.Decimal, або int (копійки). Використовувати float в оперативній пам’яті для транзакцій — це невиправданий ризик втрати точності.
У протоколі FIX тип Qty (кількість) змінили з int на float виключно через появу дробових лотів (наприклад, купівля 0.001 BTC чи 1.5 акцій). Кількість — це не гроші. Баланси рахунків та транзакційні суми (Amt) у float не рахують.
Дякую за дискусію, але я все ж таки орієнтувався на сучасні стандарти Stripe та Adyen, а не на застарілі компроміси.
А багато людей будуть писати:
На пайтон?
А пацани-то і не знають. Принаймні 2 біржі і 2 банки так роблять
Я теж був у цьому впевненим, а потім подивився, що воно з 4.2, яка 1st March, 2000. Власне, що це міняє?
Хто сказав що флоат, це швидше?
Можливо малось на увазі «може бути швидше за умови використання FPU», але якщо є умовний 256 бігінт на умовній 256 бітній архітектурі, то як може бути швидше те що помаліше?
Іншими словами: на чому міряєм, що міряєм і яким чином?
Десь так і малось на увазі:
А ще є варіант з масивами. Масив об’єктів буде і по пам’яті і по швидкодії повільніше за примітиви в багатьох випадках.
І це було б цікавою темою для статті. на відміну від тієї, що винесена в заголовок. Чи вам треба пояснення
?
і? для варіанту з масивами можуть використовуватися векторні інструкції (MMX, SSE, AVX) це на х64, а ще ж є готові векторні апаратні архітектури для векторних обчислень
Стаття для широкого загалу, а не сенйор онлі
А що всі розробники ПЗ отримали диплом бакалавра/інженера/магістра за профільною освітою?
Ну Бодя трохи перегнув у подачі але проблема із флоат настільки класична що я навіть без профільного диплому в перші місяці джуном мав досвід із цим на продакшені, а дізнався про тип децімал для грошей і проблеми які він вирішує іще коли тільки вчів дотнет та розбирався із типами даних та кастінгом.
Тож здивований що хтось при здоровому глузді юзав флоат для грошей.
Взагалі то "научпоп"/"техпоп" статті на доу не тільки для всіх, але для кожного хто може зайти на вкладку «Форум».
Он керівники бізнес юнітів основ економіки, наприклад, «закони попитиу і пропозиції» виглядає, що так щоб не потужно осилили. І нічо, статті пишуть на повних щах. А ти дивуєшся про здоровий глузд і плаваючу крапку для грошей.
1) Вчитись в 1 семестрі 1 курсу і отримати диплом — це різне
2) Постійно забуваю, точніше підсвідомо не хочу визнавати, що доу — це сайт цінний в основному для людей
— без профільної освіти,
— яким настільки насрати на те чим вони займаються, що вони не ознайомились з базовими речами по своїй спеціальності
— і власне не читають доу dou.ua/forums/topic/57695