From c1bdb3b9b5bfafce24d26b6973e368676393e5ac Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Sun, 5 Jul 2015 13:07:10 +0200 Subject: [PATCH] Add BigNumber::toScale() This allows to convert any BigNumber to a BigDecimal of the given scale. --- src/BigDecimal.php | 22 ++++++++++++++++----- src/BigInteger.php | 8 ++++++++ src/BigNumber.php | 10 ++++++++++ src/BigRational.php | 8 ++++++++ tests/BigIntegerTest.php | 24 +++++++++++++++++++++++ tests/BigRationalTest.php | 40 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 107 insertions(+), 5 deletions(-) diff --git a/src/BigDecimal.php b/src/BigDecimal.php index 0dfcf5c..8c08230 100644 --- a/src/BigDecimal.php +++ b/src/BigDecimal.php @@ -418,6 +418,8 @@ final class BigDecimal extends BigNumber implements \Serializable /** * Returns a BigDecimal with the current value and the specified scale. * + * @deprecated Use `toScale()`. + * * @param int $scale * @param int $roundingMode * @@ -425,11 +427,7 @@ final class BigDecimal extends BigNumber implements \Serializable */ public function withScale($scale, $roundingMode = RoundingMode::UNNECESSARY) { - if ($scale == $this->scale) { - return $this; - } - - return $this->dividedBy(1, $scale, $roundingMode); + return $this->toScale($scale, $roundingMode); } /** @@ -654,6 +652,20 @@ final class BigDecimal extends BigNumber implements \Serializable return BigRational::create($numerator, $denominator, false); } + /** + * {@inheritdoc} + */ + public function toScale($scale, $roundingMode = RoundingMode::UNNECESSARY) + { + $scale = (int) $scale; + + if ($scale === $this->scale) { + return $this; + } + + return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode); + } + /** * {@inheritdoc} */ diff --git a/src/BigInteger.php b/src/BigInteger.php index e9dadcf..8f1226e 100644 --- a/src/BigInteger.php +++ b/src/BigInteger.php @@ -471,6 +471,14 @@ final class BigInteger extends BigNumber implements \Serializable return BigRational::create($this, BigInteger::one(), false); } + /** + * {@inheritdoc} + */ + public function toScale($scale, $roundingMode = RoundingMode::UNNECESSARY) + { + return $this->toBigDecimal()->toScale($scale, $roundingMode); + } + /** * {@inheritdoc} */ diff --git a/src/BigNumber.php b/src/BigNumber.php index afa69f1..11f4b0a 100644 --- a/src/BigNumber.php +++ b/src/BigNumber.php @@ -354,6 +354,16 @@ abstract class BigNumber */ abstract public function toBigRational(); + /** + * Converts this number to a BigDecimal with the given scale, using rounding if necessary. + * + * @param int $scale The scale of the resulting `BigDecimal`. + * @param int $roundingMode A `RoundingMode` constant. + * + * @return BigDecimal + */ + abstract public function toScale($scale, $roundingMode = RoundingMode::UNNECESSARY); + /** * Returns the exact value of this number as a native integer. * diff --git a/src/BigRational.php b/src/BigRational.php index 9c6d257..880d98a 100644 --- a/src/BigRational.php +++ b/src/BigRational.php @@ -388,6 +388,14 @@ final class BigRational extends BigNumber implements \Serializable return $this; } + /** + * {@inheritdoc} + */ + public function toScale($scale, $roundingMode = RoundingMode::UNNECESSARY) + { + return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode); + } + /** * {@inheritdoc} */ diff --git a/tests/BigIntegerTest.php b/tests/BigIntegerTest.php index 7a391a7..0be51c2 100644 --- a/tests/BigIntegerTest.php +++ b/tests/BigIntegerTest.php @@ -1678,6 +1678,30 @@ class BigIntegerTest extends AbstractTestCase ]; } + /** + * @dataProvider providerToScale + * + * @param string $number + * @param int $scale + * @param string $expected + */ + public function testToScale($number, $scale, $expected) + { + $this->assertBigDecimalEquals($expected, BigInteger::of($number)->toScale($scale)); + } + + /** + * @return array + */ + public function providerToScale() + { + return [ + ['12345678901234567890123456789', 0, '12345678901234567890123456789'], + ['12345678901234567890123456789', 1, '12345678901234567890123456789.0'], + ['12345678901234567890123456789', 2, '12345678901234567890123456789.00'], + ]; + } + /** * @dataProvider providerToInteger * diff --git a/tests/BigRationalTest.php b/tests/BigRationalTest.php index faffe77..63eb1f9 100644 --- a/tests/BigRationalTest.php +++ b/tests/BigRationalTest.php @@ -4,6 +4,7 @@ namespace Brick\Math\Tests; use Brick\Math\BigInteger; use Brick\Math\BigRational; +use Brick\Math\RoundingMode; use Brick\Math\Exception\RoundingNecessaryException; /** @@ -833,6 +834,45 @@ class BigRationalTest extends AbstractTestCase } } + /** + * @dataProvider providerToScale + * + * @param string $number + * @param int $scale + * @param int $roundingMode + * @param string $expected + */ + public function testToScale($number, $scale, $roundingMode, $expected) + { + $number = BigRational::of($number); + + if ($this->isException($expected)) { + $this->setExpectedException($expected); + } + + $actual = $number->toScale($scale, $roundingMode); + + if (! $this->isException($expected)) { + $this->assertBigDecimalEquals($expected, $actual); + } + } + + /** + * @return array + */ + public function providerToScale() + { + return [ + ['1/8', 3, RoundingMode::UNNECESSARY, '0.125'], + ['1/16', 3, RoundingMode::UNNECESSARY, RoundingNecessaryException::class], + ['1/16', 3, RoundingMode::HALF_DOWN, '0.062'], + ['1/16', 3, RoundingMode::HALF_UP, '0.063'], + ['1/9', 30, RoundingMode::DOWN, '0.111111111111111111111111111111'], + ['1/9', 30, RoundingMode::UP, '0.111111111111111111111111111112'], + ['1/9', 100, RoundingMode::UNNECESSARY, RoundingNecessaryException::class], + ]; + } + /** * @dataProvider providerToInteger *