Showing
4 changed files
with
148 additions
and
1 deletions
| ... | @@ -50,3 +50,43 @@ composer require daito/lib:^1.0 | ... | @@ -50,3 +50,43 @@ composer require daito/lib:^1.0 |
| 50 | # or | 50 | # or |
| 51 | composer update daito/lib | 51 | composer update daito/lib |
| 52 | ``` | 52 | ``` |
| 53 | + | ||
| 54 | +## Performance note (DaitoMath) | ||
| 55 | + | ||
| 56 | +`DaitoMath` uses `brick/math` for precise decimal calculations (financial-safe). | ||
| 57 | + | ||
| 58 | +- It can run without extra PHP extensions. | ||
| 59 | +- For better performance on production, enable one of: | ||
| 60 | + - `ext-gmp` (recommended) | ||
| 61 | + - `ext-bcmath` | ||
| 62 | +- `brick/math` will automatically use the fastest available calculator at runtime. | ||
| 63 | + | ||
| 64 | +Quick check: | ||
| 65 | + | ||
| 66 | +```bash | ||
| 67 | +php -m | grep -Ei "gmp|bcmath" | ||
| 68 | +``` | ||
| 69 | + | ||
| 70 | +Install extensions if missing: | ||
| 71 | + | ||
| 72 | +```bash | ||
| 73 | +# Ubuntu / Debian | ||
| 74 | +sudo apt-get update | ||
| 75 | +sudo apt-get install -y php-gmp php-bcmath | ||
| 76 | +sudo systemctl restart php8.2-fpm || sudo systemctl restart apache2 | ||
| 77 | +``` | ||
| 78 | + | ||
| 79 | +```bash | ||
| 80 | +# CentOS / RHEL / Rocky / AlmaLinux | ||
| 81 | +sudo yum install -y php-gmp php-bcmath | ||
| 82 | +sudo systemctl restart php-fpm || sudo systemctl restart httpd | ||
| 83 | +``` | ||
| 84 | + | ||
| 85 | +```powershell | ||
| 86 | +# Windows (php.ini) | ||
| 87 | +# 1) Open php.ini | ||
| 88 | +# 2) Enable these lines: | ||
| 89 | +extension=gmp | ||
| 90 | +extension=bcmath | ||
| 91 | +# 3) Restart web server / PHP-FPM service | ||
| 92 | +``` | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file | ... | ... |
| ... | @@ -8,7 +8,12 @@ | ... | @@ -8,7 +8,12 @@ |
| 8 | } | 8 | } |
| 9 | }, | 9 | }, |
| 10 | "require": { | 10 | "require": { |
| 11 | + "brick/math": "^0.9 || ^0.10 || ^0.11 || ^0.13", | ||
| 11 | "php": "^7.3 || ^8.0", | 12 | "php": "^7.3 || ^8.0", |
| 12 | "laravel/framework": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0" | 13 | "laravel/framework": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0" |
| 14 | + }, | ||
| 15 | + "suggest": { | ||
| 16 | + "ext-gmp": "Faster big-number calculations", | ||
| 17 | + "ext-bcmath": "Faster decimal calculations when GMP is unavailable" | ||
| 13 | } | 18 | } |
| 14 | } | 19 | } | ... | ... |
| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", |
| 5 | "This file is @generated automatically" | 5 | "This file is @generated automatically" |
| 6 | ], | 6 | ], |
| 7 | - "content-hash": "f0e944fbfbea5c3b7cd02a9cd33e3cea", | 7 | + "content-hash": "0c23b9705a5fe293b678d1bbc5020207", |
| 8 | "packages": [ | 8 | "packages": [ |
| 9 | { | 9 | { |
| 10 | "name": "brick/math", | 10 | "name": "brick/math", | ... | ... |
src/DaitoMath.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +namespace Daito\Lib; | ||
| 4 | + | ||
| 5 | +use Brick\Math\BigDecimal; | ||
| 6 | +use Brick\Math\Exception\DivisionByZeroException; | ||
| 7 | +use Brick\Math\RoundingMode; | ||
| 8 | +use RuntimeException; | ||
| 9 | + | ||
| 10 | +class DaitoMath | ||
| 11 | +{ | ||
| 12 | + const DEFAULT_SCALE = 12; | ||
| 13 | + | ||
| 14 | + public static function add($left, $right, $scale = self::DEFAULT_SCALE) | ||
| 15 | + { | ||
| 16 | + $bigDecimal = self::toBigDecimal($left)->plus(self::toBigDecimal($right)); | ||
| 17 | + | ||
| 18 | + return self::normalize((string) $bigDecimal->toScale((int) $scale)); | ||
| 19 | + } | ||
| 20 | + | ||
| 21 | + public static function sub($left, $right, $scale = self::DEFAULT_SCALE) | ||
| 22 | + { | ||
| 23 | + $bigDecimal = self::toBigDecimal($left)->minus(self::toBigDecimal($right)); | ||
| 24 | + | ||
| 25 | + return self::normalize((string) $bigDecimal->toScale((int) $scale)); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + public static function mul($left, $right, $scale = self::DEFAULT_SCALE) | ||
| 29 | + { | ||
| 30 | + $bigDecimal = self::toBigDecimal($left)->multipliedBy(self::toBigDecimal($right)); | ||
| 31 | + | ||
| 32 | + return self::normalize((string) $bigDecimal->toScale((int) $scale)); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + public static function div($left, $right, $scale = self::DEFAULT_SCALE) | ||
| 36 | + { | ||
| 37 | + $leftValue = self::toBigDecimal($left); | ||
| 38 | + $rightValue = self::toBigDecimal($right); | ||
| 39 | + | ||
| 40 | + if ($rightValue->isZero()) { | ||
| 41 | + throw new RuntimeException('Can not divide by zero.'); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + try { | ||
| 45 | + $bigDecimal = $leftValue->dividedBy($rightValue, (int) $scale, RoundingMode::HALF_UP); | ||
| 46 | + } catch (DivisionByZeroException $exception) { | ||
| 47 | + throw new RuntimeException('Can not divide by zero.'); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + return self::normalize((string) $bigDecimal); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | + public static function floor($value) | ||
| 54 | + { | ||
| 55 | + return (string) self::toBigDecimal($value)->toScale(0, RoundingMode::FLOOR); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + public static function ceil($value) | ||
| 59 | + { | ||
| 60 | + return (string) self::toBigDecimal($value)->toScale(0, RoundingMode::CEILING); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + public static function round($value, $scale = 0) | ||
| 64 | + { | ||
| 65 | + return self::normalize((string) self::toBigDecimal($value)->toScale((int) $scale, RoundingMode::HALF_UP)); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + public static function mulFloor($left, $right, $scale = self::DEFAULT_SCALE) | ||
| 69 | + { | ||
| 70 | + return self::floor(self::mul($left, $right, $scale)); | ||
| 71 | + } | ||
| 72 | + | ||
| 73 | + private static function toBigDecimal($value) | ||
| 74 | + { | ||
| 75 | + if (is_int($value) || is_string($value)) { | ||
| 76 | + return BigDecimal::of((string) $value); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + if (is_float($value)) { | ||
| 80 | + return BigDecimal::of(self::normalize(sprintf('%.14F', $value))); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + throw new RuntimeException('DaitoMath only supports int, float, or numeric string.'); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + private static function normalize($number) | ||
| 87 | + { | ||
| 88 | + $number = (string) $number; | ||
| 89 | + if (strpos($number, '.') === false) { | ||
| 90 | + return $number; | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + $number = rtrim($number, '0'); | ||
| 94 | + $number = rtrim($number, '.'); | ||
| 95 | + | ||
| 96 | + if ($number === '-0') { | ||
| 97 | + return '0'; | ||
| 98 | + } | ||
| 99 | + | ||
| 100 | + return $number === '' ? '0' : $number; | ||
| 101 | + } | ||
| 102 | +} |
-
Please register or sign in to post a comment