Skip to content

Commit

Permalink
Allow negative scale in BigDecimal::ofUnscaledValue()
Browse files Browse the repository at this point in the history
  • Loading branch information
BenMorel committed Feb 28, 2025
1 parent 88500fc commit d0a5c66
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 32 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to this project will be documented in this file.

## UNRELEASED (0.13.0)

💥 **Breaking changes**

- `BigDecimal::ofUnscaledValue()` no longer throws an exception if the scale is negative

**New features**

- `BigDecimal::ofUnscaledValue()` allows a negative scale (and converts the values to create a zero scale number)

## [0.12.3](https://github.com/brick/math/releases/tag/0.12.3) - 2025-02-28

**New features**
Expand Down
14 changes: 9 additions & 5 deletions src/BigDecimal.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,23 @@ protected static function from(BigNumber $number): static
* Example: `(12345, 3)` will result in the BigDecimal `12.345`.
*
* @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
* @param int $scale The scale of the number, positive or zero.
*
* @throws \InvalidArgumentException If the scale is negative.
* @param int $scale The scale of the number. If negative, the scale will be set to zero
* and the unscaled value will be adjusted accordingly.
*
* @psalm-pure
*/
public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal
{
$value = (string) BigInteger::of($value);

if ($scale < 0) {
throw new \InvalidArgumentException('The scale cannot be negative.');
if ($value !== '0') {
$value .= \str_repeat('0', -$scale);
}
$scale = 0;
}

return new BigDecimal((string) BigInteger::of($value), $scale);
return new BigDecimal($value, $scale);
}

/**
Expand Down
115 changes: 88 additions & 27 deletions tests/BigDecimalTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -269,48 +269,109 @@ public function testOfBigDecimalReturnsThis() : void
* @param int|string $unscaledValue The unscaled value of the BigDecimal to create.
* @param int $scale The scale of the BigDecimal to create.
* @param string $expectedUnscaledValue The expected result unscaled value.
* @param int $expectedScale The expected result scale.
*/
#[DataProvider('providerOfUnscaledValue')]
public function testOfUnscaledValue(int|string $unscaledValue, int $scale, string $expectedUnscaledValue) : void
{
public function testOfUnscaledValue(
int|string $unscaledValue,
int $scale,
string $expectedUnscaledValue,
int $expectedScale,
) : void {
$number = BigDecimal::ofUnscaledValue($unscaledValue, $scale);
self::assertBigDecimalInternalValues($expectedUnscaledValue, $scale, $number);
self::assertBigDecimalInternalValues($expectedUnscaledValue, $expectedScale, $number);
}

public static function providerOfUnscaledValue() : array
{
return [
[123456789, 0, '123456789'],
[123456789, 1, '123456789'],
[-123456789, 0, '-123456789'],
[-123456789, 1, '-123456789'],

['123456789012345678901234567890', 0, '123456789012345678901234567890'],
['123456789012345678901234567890', 1, '123456789012345678901234567890'],
['+123456789012345678901234567890', 0, '123456789012345678901234567890'],
['+123456789012345678901234567890', 1, '123456789012345678901234567890'],
['-123456789012345678901234567890', 0, '-123456789012345678901234567890'],
['-123456789012345678901234567890', 1, '-123456789012345678901234567890'],

['0123456789012345678901234567890', 0, '123456789012345678901234567890'],
['0123456789012345678901234567890', 1, '123456789012345678901234567890'],
['+0123456789012345678901234567890', 0, '123456789012345678901234567890'],
['+0123456789012345678901234567890', 1, '123456789012345678901234567890'],
['-0123456789012345678901234567890', 0, '-123456789012345678901234567890'],
['-0123456789012345678901234567890', 1, '-123456789012345678901234567890'],
[0, -2, '0', 0],
[0, -1, '0', 0],
[0, 0, '0', 0],
[0, 1, '0', 1],
[0, 2, '0', 2],

[123456789, -2, '12345678900', 0],
[123456789, -1, '1234567890', 0],
[123456789, 0, '123456789', 0],
[123456789, 1, '123456789', 1],

[-123456789, -2, '-12345678900', 0],
[-123456789, -1, '-1234567890', 0],
[-123456789, 0, '-123456789', 0],
[-123456789, 1, '-123456789', 1],

['123456789012345678901234567890', -1, '1234567890123456789012345678900', 0],
['123456789012345678901234567890', 0, '123456789012345678901234567890', 0],
['123456789012345678901234567890', 1, '123456789012345678901234567890', 1],
['+123456789012345678901234567890', -1, '1234567890123456789012345678900', 0],
['+123456789012345678901234567890', 0, '123456789012345678901234567890', 0],
['+123456789012345678901234567890', 1, '123456789012345678901234567890', 1],
['-123456789012345678901234567890', -1, '-1234567890123456789012345678900', 0],
['-123456789012345678901234567890', 0, '-123456789012345678901234567890', 0],
['-123456789012345678901234567890', 1, '-123456789012345678901234567890', 1],

['0123456789012345678901234567890', -1, '1234567890123456789012345678900', 0],
['0123456789012345678901234567890', 0, '123456789012345678901234567890', 0],
['0123456789012345678901234567890', 1, '123456789012345678901234567890', 1],
['+0123456789012345678901234567890', -1, '1234567890123456789012345678900', 0],
['+0123456789012345678901234567890', 0, '123456789012345678901234567890', 0],
['+0123456789012345678901234567890', 1, '123456789012345678901234567890', 1],
['-0123456789012345678901234567890', -1, '-1234567890123456789012345678900', 0],
['-0123456789012345678901234567890', 0, '-123456789012345678901234567890', 0],
['-0123456789012345678901234567890', 1, '-123456789012345678901234567890', 1],
];
}

public function testOfUnscaledValueWithDefaultScale() : void
#[DataProvider('providerOfUnscaledValueToString')]
public function testOfUnscaledValueToString(string $unscaledValue, int $scale, string $expected) : void
{
$number = BigDecimal::ofUnscaledValue('123456789');
self::assertBigDecimalInternalValues('123456789', 0, $number);
self::assertSame($expected, (string) BigDecimal::ofUnscaledValue($unscaledValue, $scale));
}

public function testOfUnscaledValueWithNegativeScaleThrowsException() : void
public static function providerOfUnscaledValueToString() : array
{
$this->expectException(\InvalidArgumentException::class);
BigDecimal::ofUnscaledValue('0', -1);
return [
['-1', -1, '-10'],
['-1', 0, '-1'],
['-1', 1, '-0.1'],

['-0', -1, '0'],
['-0', 0, '0'],
['-0', 1, '0.0'],

['0', -1, '0'],
['0', 0, '0'],
['0', 1, '0.0'],

['1', -1, '10'],
['1', 0, '1'],
['1', 1, '0.1'],

['-123', -3, '-123000'],
['-123', -2, '-12300'],
['-123', -1, '-1230'],
['-123', 0, '-123'],
['-123', 1, '-12.3'],
['-123', 2, '-1.23'],
['-123', 3, '-0.123'],
['-123', 4, '-0.0123'],

['123', -3, '123000'],
['123', -2, '12300'],
['123', -1, '1230'],
['123', 0, '123'],
['123', 1, '12.3'],
['123', 2, '1.23'],
['123', 3, '0.123'],
['123', 4, '0.0123'],
];
}

public function testOfUnscaledValueWithDefaultScale() : void
{
$number = BigDecimal::ofUnscaledValue('123456789');
self::assertBigDecimalInternalValues('123456789', 0, $number);
}

public function testZero() : void
Expand Down

0 comments on commit d0a5c66

Please sign in to comment.