diff --git a/docs/reference/api.md b/docs/reference/api.md index e4677d8..9de3233 100644 --- a/docs/reference/api.md +++ b/docs/reference/api.md @@ -25,8 +25,8 @@ See [Creating decimals](../guide/creating). | `multiply($value, ?int $scale = null): static` | Product | sum of both | | `divide($value, int $scale, ?int $roundMode = null): static` | Quotient, cut off at `$scale`, or rounded when a mode is given | required | | `divideWithRemainder($value): array` | Whole number quotient and remainder | 0, and larger of both | -| `mod($value, ?int $scale = null): static` | Remainder. `$value` is a `Decimal`, string or integer. | scale of the value | -| `pow($exponent, ?int $scale = null): static` | Power. `$exponent` is a whole number as `Decimal`, string or integer. | scale of the value | +| `mod($value, ?int $scale = null): static` | Remainder | scale of the value | +| `pow($exponent, ?int $scale = null): static` | Power. `$exponent` is a whole number. | scale of the value | | `sqrt(?int $scale = null): static` | Square root | scale of the value | | `shift(int $places): static` | Moves the decimal point, multiplying by a power of ten | follows the digits | | `absolute(): static` | Value without sign | unchanged | diff --git a/src/Decimal.php b/src/Decimal.php index 8e7db89..ccbcbd2 100644 --- a/src/Decimal.php +++ b/src/Decimal.php @@ -775,12 +775,12 @@ protected function fractionalLength(): int /** * This method is equivalent to the ** operator. * - * @param self|string|int $exponent + * @param self|string|float|int $exponent * @param int|null $scale Decimal places in the result. Omit to enable auto-detection. * * @return static */ - public function pow(self|string|int $exponent, ?int $scale = null): static + public function pow(self|string|float|int $exponent, ?int $scale = null): static { if ($scale === null) { $scale = $this->scale(); @@ -808,12 +808,12 @@ public function sqrt(?int $scale = null): static /** * This method is equivalent to the % operator. * - * @param self|string|int $value + * @param self|string|float|int $value * @param int|null $scale Decimal places in the result. Omit to enable auto-detection. * * @return static */ - public function mod(self|string|int $value, ?int $scale = null): static + public function mod(self|string|float|int $value, ?int $scale = null): static { if ($scale === null) { $scale = $this->scale(); diff --git a/tests/DecimalTest.php b/tests/DecimalTest.php index 9585c31..2ccdc6b 100644 --- a/tests/DecimalTest.php +++ b/tests/DecimalTest.php @@ -17,6 +17,7 @@ use PHPUnit\Framework\TestCase; use Stringable; use TypeError; +use ValueError; class DecimalTest extends TestCase { @@ -1981,4 +1982,45 @@ public function testAllocateWithZeroScaleDeclared(): void $this->assertSame(['0.8', '0.7'], array_map('strval', $shares)); } + + /** + * Without `float` in the parameter type PHP turns 2.5 into 2 for callers not using strict types. + * + * @return void + */ + public function testModFloatOperand(): void + { + $this->assertSame('2.0', (string)(new Decimal('7'))->mod(2.5, 1)); + $this->assertSame('2.0', (string)(new Decimal('7.0'))->mod(2.5)); + $this->assertSame('1', (string)(new Decimal('7'))->mod(2.0)); + } + + /** + * @return void + */ + public function testPowFloatOperand(): void + { + $this->assertSame('16', (string)(new Decimal('4'))->pow(2.0)); + } + + /** + * @return void + */ + public function testPowFractionalFloatOperand(): void + { + $this->expectException(ValueError::class); + + (new Decimal('4'))->pow(0.5); + } + + /** + * @return void + */ + public function testFloatOperandsAreNotTruncatedWithoutStrictTypes(): void + { + $result = require __DIR__ . '/data/float_operands.php'; + + $this->assertSame('2.0', $result['mod']); + $this->assertSame(ValueError::class, $result['pow']); + } } diff --git a/tests/data/float_operands.php b/tests/data/float_operands.php new file mode 100644 index 0000000..ac11301 --- /dev/null +++ b/tests/data/float_operands.php @@ -0,0 +1,20 @@ + (string)(new Decimal('7.0'))->mod(2.5)]; + +try { + $result['pow'] = (string)(new Decimal('4'))->pow(0.5); +} catch (Throwable $e) { + $result['pow'] = $e::class; +} + +return $result;