Skip to content

Commit

Permalink
Refactor and fix to unify behavior to PHP 8.4
Browse files Browse the repository at this point in the history
  • Loading branch information
SakiTakamachi committed Dec 9, 2024
1 parent 79b2c12 commit 8eb81b4
Show file tree
Hide file tree
Showing 8 changed files with 30 additions and 158 deletions.
57 changes: 27 additions & 30 deletions src/BcMath/Number.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,13 @@ private function getMetaData(): array
private function getRealValue(): string
{
$metaData = $this->getMetaData();
return $metaData['value'];
return $metaData[0];
}

private function getRealScale(): int
{
$metaData = $this->getMetaData();
return $metaData['scale'];
}

private function isNegative(): bool
{
return substr($this->getRealValue(), 0, 1) === '-';
return $metaData[1];
}

private function quickThreeWayComparisonWithZero(): int
Expand Down Expand Up @@ -184,8 +179,8 @@ private function doAddSub(bool $isAdd, Number|string|int $num, ?int $scale = nul
$num = $num instanceof Number ? $num : new Number($num);
$scale = $scale ?? max($this->scale, $num->scale);
$result = $isAdd
? bcadd($this->getRealValue(), $num->value, $scale)
: bcsub($this->getRealValue(), $num->value, $scale);
? bcadd($this->getRealValue(), $num->getRealValue(), $scale)
: bcsub($this->getRealValue(), $num->getRealValue(), $scale);
return new Number($result);
}

Expand All @@ -199,7 +194,7 @@ public function mul(Number|string|int $num, ?int $scale = null): Number
}
}
$scale = $scale ?? $this->scale + $num->scale;
$result = bcmul($this->getRealValue(), $num->value, $scale);
$result = bcmul($this->getRealValue(), $num->getRealValue(), $scale);
return new Number($result);
}

Expand Down Expand Up @@ -230,7 +225,7 @@ public function div(Number|string|int $num, ?int $scale = null): Number
$this->throwError(ErrorType::TooLargeScale);
}
}
$result = bcdiv($this->getRealValue(), $num->value, $scale);
$result = bcdiv($this->getRealValue(), $num->getRealValue(), $scale);
return new Number(
$scaleIsNull
? $this->removeUnnecessaryExtendedScale($result)
Expand All @@ -242,7 +237,7 @@ public function mod(Number|string|int $num, ?int $scale = null): Number
{
$num = $num instanceof Number ? $num : new Number($num);
$scale = $scale ?? max($this->scale, $num->scale);
$result = bcmod($this->getRealValue(), $num->value, $scale);
$result = bcmod($this->getRealValue(), $num->getRealValue(), $scale);
return new Number($result);
}

Expand All @@ -252,8 +247,8 @@ public function divmod(Number|string|int $num, ?int $scale = null): array
$num = $num instanceof Number ? $num : new Number($num);
$scale = $scale ?? max($this->scale, $num->scale);
$realValue = $this->getRealValue();
$quot = bcdiv($realValue, $num->value, 0);
$rem = bcmod($realValue, $num->value, $scale);
$quot = bcdiv($realValue, $num->getRealValue(), 0);
$rem = bcmod($realValue, $num->getRealValue(), $scale);
return [
new Number($quot),
new Number($rem),
Expand All @@ -262,25 +257,28 @@ public function divmod(Number|string|int $num, ?int $scale = null): array

public function powmod(Number|string|int $exponent, Number|string|int $modulus, ?int $scale = null): Number
{
if (ScaleChecker::hasFractionalPart($this->scale, $this->getRealScale())) {
[$realValue, $realScale] = $this->getMetaData();
if ($realScale > 0) {
throw new ValueError('Base number cannot have a fractional part');
}

$exponent = $exponent instanceof Number ? $exponent : new Number($exponent);
if (ScaleChecker::hasFractionalPart($exponent->scale, $exponent->getRealScale())) {
[$exponentRealValue, $exponentRealScale] = $exponent->getMetaData();
if ($exponentRealScale > 0) {
$this->throwError(ErrorType::PowmodHasFractionParts, __METHOD__, 'exponent', 1);
}
if ($exponent->isNegative()) {
if ($exponent->quickThreeWayComparisonWithZero() === -1) {
$this->throwError(ErrorType::PowmodExponentIsNegative, __METHOD__, 'exponent', 1);
}

$modulus = $modulus instanceof Number ? $modulus : new Number($modulus);
if (ScaleChecker::hasFractionalPart($modulus->scale, $modulus->getRealScale())) {
[$modulusRealValue, $modulusRealScale] = $modulus->getMetaData();
if ($modulusRealScale > 0) {
$this->throwError(ErrorType::PowmodHasFractionParts, __METHOD__, 'modulus', 2);
}

$scale = $scale ?? 0;
$result = bcpowmod($this->getRealValue(), $exponent->value, $modulus->value, $scale);
$result = bcpowmod($realValue, $exponentRealValue, $modulusRealValue, $scale);
return new Number($result);
}

Expand All @@ -300,7 +298,7 @@ public function pow(Number|string|int $exponent, ?int $scale = null): Number
$this->throwError(ErrorType::TooLargeScale);
}
}
$result = bcpow($this->getRealValue(), $exponent->value, $scale);
$result = bcpow($this->getRealValue(), $exponent->getRealValue(), $scale);
return new Number(
$scaleIdNull
? $this->removeUnnecessaryExtendedScale($result)
Expand All @@ -312,13 +310,13 @@ public function pow(Number|string|int $exponent, ?int $scale = null): Number
return new Number('1' . $fraction);
case 1:
if (!isset($scale)) {
$scaleStr = bcmul((string) $this->scale, $exponent->value);
$scaleStr = bcmul((string) $this->scale, $exponent->getRealValue());
$scale = (int) $scaleStr;
if ((string) $scale !== $scaleStr || ScaleChecker::isOverflow($scale, $this->scale)) {
$this->throwError(ErrorType::TooLargeScale);
}
}
return new Number(bcpow($this->getRealValue(), $exponent->value, $scale));
return new Number(bcpow($this->getRealValue(), $exponent->getRealValue(), $scale));
}
}

Expand All @@ -337,7 +335,7 @@ public function sqrt(?int $scale = null): Number
public function floor(): Number
{
$realValue = $this->getRealValue();
if (!$this->isNegative()) {
if ($this->quickThreeWayComparisonWithZero() >= 0) {
[$result] = explode('.', $realValue);
} else {
if ($this->getRealScale() > 0) {
Expand All @@ -352,7 +350,7 @@ public function floor(): Number
public function ceil(): Number
{
$realValue = $this->getRealValue();
if (!$this->isNegative()) {
if ($this->quickThreeWayComparisonWithZero() >= 0) {
if ($this->getRealScale() > 0) {
$result = bcadd($realValue, '1', 0);
} else {
Expand All @@ -366,10 +364,7 @@ public function ceil(): Number

public function round(int $precision = 0, RoundingMode $mode = RoundingMode::HalfAwayFromZero): Number
{
[
'value' => $realValue,
'scale' => $realScale,
] = $this->getMetaData();
[$realValue, $realScale] = $this->getMetaData();
if ($realScale <= $precision) {
$trailingZeroes = ($realScale === 0 ? '.' : '') . str_repeat('0', $precision - $realScale);
return new Number($realValue . $trailingZeroes);
Expand Down Expand Up @@ -553,8 +548,10 @@ public function round(int $precision = 0, RoundingMode $mode = RoundingMode::Hal
public function compare(Number|string|int $num, ?int $scale = null): int
{
$num = $num instanceof Number ? $num : new Number($num);
$scale = $scale ?? max($this->scale, $num->scale);
return bccomp($this->getRealValue(), $num->value, $scale);
[$realValue, $realScale] = $this->getMetaData();
[$numRealValue, $numRealScale] = $num->getMetaData();
$scale = $scale ?? max($realScale, $numRealScale);
return bccomp($realValue, $numRealValue, $scale);
}

public function __toString(): string
Expand Down
5 changes: 1 addition & 4 deletions src/Number/MetaData.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,6 @@ public static function setMetaData(Number $object, string $value, int $scale): v
throw new LogicException('Metadata already set');
}

self::$metaData[$hash] = [
'value' => $value,
'scale' => $scale,
];
self::$metaData[$hash] = [$value, $scale];
}
}
6 changes: 0 additions & 6 deletions src/Number/ScaleChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,10 @@

namespace Saki\Number;

use Saki\Number\ScaleCheckerTraits\HasFractionalPart;

if (PHP_INT_SIZE === 8) {
// for 64-bit
class ScaleChecker
{
use HasFractionalPart;

private const INT_MAX = 2147483647;
public static function isOverflow(int $scale, int $originScale): bool
{
Expand All @@ -20,8 +16,6 @@ public static function isOverflow(int $scale, int $originScale): bool
// for 32-bit
class ScaleChecker
{
use HasFractionalPart;

public static function isOverflow(int $scale, int $originScale): bool
{
return $scale > PHP_INT_MAX || $scale < $originScale;
Expand Down
34 changes: 0 additions & 34 deletions src/Number/ScaleCheckerTraits/HasFractionalPart.php

This file was deleted.

34 changes: 0 additions & 34 deletions tests/pow/8.2/pow_negative_power_of_zero.phpt

This file was deleted.

36 changes: 0 additions & 36 deletions tests/pow/8.2/powmod_by_zero.phpt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
--TEST--
pow() - negative power of zero for PHP-8.3
--SKIPIF--
<?php
if (PHP_MAJOR_VERSION !== 8 || PHP_MINOR_VERSION !== 3) {
die('skip only PHP-8.3');
}
?>
--FILE--
<?php
require_once __DIR__ . '/../../include.inc';
require_once __DIR__ . '/../include.inc';

use BcMath\Number;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
--TEST--
powmod() - modulus by zero for PHP-8.3
--SKIPIF--
<?php
if (PHP_MAJOR_VERSION !== 8 || PHP_MINOR_VERSION !== 3) {
die('skip only PHP-8.3');
}
?>
--FILE--
<?php
require_once __DIR__ . '/../../include.inc';
require_once __DIR__ . '/../include.inc';

use BcMath\Number;

Expand Down

0 comments on commit 8eb81b4

Please sign in to comment.