From 21abae165a855bc3ce1d76e5e6129aa3d2a32e5b Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 10 Mar 2025 03:24:56 +0100 Subject: [PATCH 1/2] Fix up rounding for small values. --- phpstan.neon | 2 +- src/Decimal.php | 34 ++++++++++++++++++++++++++++------ tests/DecimalTest.php | 28 +++++++++++++++++++++++++--- 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index e77e10e..aa161e6 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,4 +5,4 @@ parameters: ignoreErrors: - identifier: missingType.iterableValue - '#Unsafe usage of new static\(\).#' - - '#expects numeric-string, string given.#' + - '#expects numeric-string, .*string given.#' diff --git a/src/Decimal.php b/src/Decimal.php index 00ad492..ac9b2d5 100644 --- a/src/Decimal.php +++ b/src/Decimal.php @@ -485,22 +485,44 @@ public function round(int $scale = 0, int $roundMode = self::ROUND_HALF_UP): sta { $exponent = $scale + 1; - $e = bcpow('10', (string)$exponent); switch ($roundMode) { case static::ROUND_FLOOR: - $v = bcdiv(bcadd(bcmul((string)$this, $e, 0), $this->isNegative() ? '-9' : '0'), $e, 0); + $stringValue = (string)$this; - break; + // If already an integer, return as is + if (!str_contains($stringValue, '.')) { + return new static($stringValue, $scale); + } + + // For positive values, truncate the decimal part (round down) + if (!$this->isNegative()) { + return new static(bcsub($stringValue, bcmod($stringValue, '1'), 0), $scale); + } + + // For negative values, round down away from zero + return new static(bcsub($stringValue, '1', 0), $scale); case static::ROUND_CEIL: - $v = bcdiv(bcadd(bcmul((string)$this, $e, 0), $this->isNegative() ? '0' : '9'), $e, 0); + $stringValue = (string)$this; + + // If already an integer, return as is + if (!str_contains($stringValue, '.')) { + return new static($stringValue, $scale); + } + + // If negative, truncate (remove decimals without adding 1) + if ($this->isNegative()) { + return new static(bcsub($stringValue, '0', 0), $scale); + } - break; + // Otherwise, round up for positive numbers + return new static(bcadd($stringValue, '1', 0), $scale); case static::ROUND_HALF_UP: default: + $e = bcpow('10', (string)$exponent); $v = bcdiv(bcadd(bcmul((string)$this, $e, 0), $this->isNegative() ? '-5' : '5'), $e, $scale); } - return new static($v); + return new static($v, $scale); } /** diff --git a/tests/DecimalTest.php b/tests/DecimalTest.php index 8da6e9f..106a109 100644 --- a/tests/DecimalTest.php +++ b/tests/DecimalTest.php @@ -636,7 +636,12 @@ public function testRound(mixed $value, int $scale, string $expected): void */ protected function assertNativeRound(string $expected, mixed $value, int $scale, int $roundMode): void { - $this->assertSame((new Decimal($expected))->trim()->toString(), (string)round((float)$value, $scale, $roundMode)); + $value = (string)round((float)$value, $scale, $roundMode); + if ($value === '-0') { + $value = '0'; + } + + $this->assertSame((new Decimal($expected))->trim()->toString(), $value); } /** @@ -659,6 +664,10 @@ public static function roundProvider(): array [13.4999, 0, '13'], [13.4999, 10, '13.4999000000'], [13.4999, 2, '13.50'], + [0.0001, 0, '0'], + [-0.0001, 0, '0'], + [0.9999, 0, '1'], + [-0.9999, 0, '-1'], ]; } @@ -685,7 +694,12 @@ public function testFloor(mixed $value, string $expected): void */ protected function assertNativeFloor(string $expected, mixed $value): void { - $this->assertSame($expected, (string)floor((float)$value)); + $value = (string)floor((float)$value); + if ($value === '-0') { + $value = '0'; + } + + $this->assertSame($expected, $value); } /** @@ -706,6 +720,8 @@ public static function floorProvider(): array ['13.6999', '13'], ['13.1', '13'], ['13.9', '13'], + [0.0001, '0'], + [-0.0001, '-1'], ]; } @@ -732,7 +748,11 @@ public function testCeil(mixed $value, string $expected): void */ protected function assertNativeCeil(string $expected, mixed $value): void { - $this->assertSame($expected, (string)ceil((float)$value)); + $value = (string)ceil((float)$value); + if ($value === '-0') { + $value = '0'; + } + $this->assertSame($expected, $value); } /** @@ -753,6 +773,8 @@ public static function ceilProvider(): array ['13.6999', '14'], ['13.1', '14'], ['13.9', '14'], + [0.0001, '1'], + [-0.0001, '0'], ]; } From eeb19aa8dd3a407996d09193192e7adda6f06063 Mon Sep 17 00:00:00 2001 From: mscherer Date: Mon, 10 Mar 2025 03:27:23 +0100 Subject: [PATCH 2/2] Fix up rounding for small values. --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2bfe7d0..c168dc2 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "require-dev": { "php-collective/code-sniffer": "^0.2.1", "phpstan/phpstan": "^2.0.0", - "phpunit/phpunit": "^11.0.0" + "phpunit/phpunit": "^10.5.45 || ^11.0.0" }, "minimum-stability": "stable", "prefer-stable": true,