Как правильно считать проценты для цен в PHP

В этой статье я расскажу, как удобно и точно работать с процентами для цен: скидки, наценки, доля от суммы или процентное изменение. Приведу рабочие примеры на PHP 8+ и дам советы, как безопасно обращаться с дробными числами и аккуратно отображать результаты в интернет-магазине.
1. Основные формулы для процентов
Чтобы корректно работать с ценами, полезно помнить базовые формулы:
- Процент от суммы:
x = сумма * (процент / 100) - Добавить процент к цене:
x = сумма * (1 + процент / 100) - Уменьшить цену на процент:
x = сумма * (1 - процент / 100) - Изменение в процентах между старой и новой ценой:
процент = ((новая - старая) / старая) * 100
Эти формулы просты, но важно учитывать точность вычислений и правильные типы данных.
2. Почему float может подвести
Использование float для цен может приводить к неточностям (например, 0.1 + 0.2 ≠ 0.3). Чтобы избежать проблем:
- Храните цены в целых единицах — копейках или центах
int. - Или используйте
BCMathдля точных операций с десятичными числами.
3. Практичные функции на PHP 8+
<?php
declare(strict_types=1);
function percentOf(int $amountCents, float $percent): int {
return (int) round($amountCents * ($percent / 100), 0, PHP_ROUND_HALF_UP);
}
function addPercent(int $amountCents, float $percent): int {
return $amountCents + percentOf($amountCents, $percent);
}
function subtractPercent(int $amountCents, float $percent): int {
$res = $amountCents - percentOf($amountCents, $percent);
return $res >= 0 ? $res : 0;
}
function percentChange(int $oldCents, int $newCents): ?float {
if ($oldCents === 0) return null;
return (($newCents - $oldCents) / $oldCents) * 100.0;
}
function formatMoney(int $amountCents, int $decimals = 2): string {
return number_format($amountCents / 100, $decimals, '.', ' ');
}
?>
Пример использования:
$price = 100000; // 1000.00 у.е. в копейках
$discount = 20.0; // 20%
$newPrice = subtractPercent($price, $discount); // 80000
echo formatMoney($newPrice); // "800.00"
4. Точный вариант с BCMath
function bc_add_percent(string $amount, string $percent, int $scale = 2): string {
$mult = bcdiv($percent, "100", $scale + 2);
$inc = bcmul($amount, $mult, $scale + 2);
return bcadd($amount, $inc, $scale);
}
echo bc_add_percent("1000.00", "12.5", 2); // "1125.00"
BCMath идеально подходит, если цены хранятся как строки и важна максимальная точность.
5. Советы для практики
- Храните цены в копейках/центах — проще и безопаснее.
- Округляйте только в конце, чтобы накопленная точность не терялась.
- Используйте
PHP_ROUND_HALF_UPдля финансовых операций. - Проверяйте крайние случаи: ноль, отрицательные значения, большие числа.
- Будьте прозрачны: показывайте старую цену, сумму скидки и процент.
- Если используете налоги, решите, считать их до или после скидки, и соблюдайте единообразие.
6. Примеры сценариев
- Скидка 15% на товар 749.99 → храните 74999 (копейки),
subtractPercent(74999, 15.0)→ округлённый результат. - Наценка 25% → используйте
addPercent(). Чтобы найти базовую цену:base = price / (1 + p/100). - Процент изменения цены →
percentChange(), если старая цена 0 — решите, как обрабатывать (null или бесконечный рост).
7. Заключение
Формулы простые, но реализация требует аккуратности с типами и округлением. Для интернет-магазина лучше хранить цены в копейках и использовать типизированные функции, как показано выше. Если нужно, можно сделать компактный класс-обёртку под конкретный проект, чтобы автоматически работать со скидками, налогами и форматированием.
Чтобы получить полный доступ, зарегистрируйтесь или войдите под своим именем.
