1
0
mirror of https://github.com/danog/math.git synced 2025-01-22 13:41:12 +01:00

Exact division algorithm is now in BigDecimal::dividedBy()

BigRational::toBigDecimal() is now really just a proxy method to BigDecimal::dividedBy().
This commit is contained in:
Benjamin Morel 2015-06-28 22:51:02 +02:00
parent 5ccc6641b5
commit 1420202440
3 changed files with 41 additions and 41 deletions

View File

@ -150,7 +150,31 @@ final class BigDecimal extends BigNumber implements \Serializable
{ {
$that = BigDecimal::of($that); $that = BigDecimal::of($that);
$result = $this->toBigRational()->dividedBy($that->toBigRational())->toBigDecimal(); if ($that->value === '0') {
throw DivisionByZeroException::denominatorMustNotBeZero();
}
$this->scaleValues($this, $that, $a, $b);
$d = rtrim($b, '0');
$scale = strlen($b) - strlen($d);
$calculator = Calculator::get();
foreach ([5, 2] as $prime) {
for (;;) {
$lastDigit = (int) substr($d, -1);
if ($lastDigit % $prime !== 0) {
break;
}
list ($d) = $calculator->div($d, (string) $prime);
$scale++;
}
}
$result = $this->dividedToScale($that, $scale)->stripTrailingZeros();
if ($result->scale < $this->scale) { if ($result->scale < $this->scale) {
$result = $result->withScale($this->scale); $result = $result->withScale($this->scale);

View File

@ -271,37 +271,7 @@ final class BigRational extends BigNumber implements \Serializable
*/ */
public function toBigDecimal() public function toBigDecimal()
{ {
$simplified = $this->simplified(); return $this->numerator->toBigDecimal()->dividedBy($this->denominator);
$denominator = $simplified->denominator;
$counts = [
2 => 0,
5 => 0
];
foreach ([2, 5] as $divisor) {
do {
list ($quotient, $remainder) = $denominator->quotientAndRemainder($divisor);
if ($remainderIsZero = $remainder->isZero()) {
$denominator = $quotient;
$counts[$divisor]++;
}
}
while ($remainderIsZero);
}
if (! $denominator->isEqualTo(1)) {
throw new RoundingNecessaryException('This rational number cannot be represented as a finite decimal number without rounding.');
}
$maxDecimalPlaces = max($counts[2], $counts[5]);
return $simplified->numerator
->toBigDecimal()
->dividedToScale($simplified->denominator, $maxDecimalPlaces)
->stripTrailingZeros();
} }
/** /**

View File

@ -3,6 +3,7 @@
namespace Brick\Math\Tests; namespace Brick\Math\Tests;
use Brick\Math\BigDecimal; use Brick\Math\BigDecimal;
use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\RoundingMode; use Brick\Math\RoundingMode;
use Brick\Math\Exception\RoundingNecessaryException; use Brick\Math\Exception\RoundingNecessaryException;
@ -633,17 +634,20 @@ class BigDecimalTest extends AbstractTestCase
* *
* @param string|number $number The number to divide. * @param string|number $number The number to divide.
* @param string|number $divisor The divisor. * @param string|number $divisor The divisor.
* @param string|null $expected The expected result, or null if an exception is expected. * @param string $expected The expected result, or a class name if an exception is expected.
*/ */
public function testDividedBy($number, $divisor, $expected) public function testDividedBy($number, $divisor, $expected)
{ {
if ($expected === null) { $number = BigDecimal::of($number);
$this->setExpectedException(RoundingNecessaryException::class);
if ($this->isException($expected)) {
$this->setExpectedException($expected);
} }
$actual = BigDecimal::of($number)->dividedBy($divisor); $actual = $number->dividedBy($divisor);
if ($expected !== null) { if (! $this->isException($expected)) {
$this->assertInstanceOf(BigDecimal::class, $actual);
$this->assertSame($expected, (string) $actual); $this->assertSame($expected, (string) $actual);
} }
} }
@ -657,13 +661,13 @@ class BigDecimalTest extends AbstractTestCase
[1, 1, '1'], [1, 1, '1'],
['1.0', '1.00', '1.0'], ['1.0', '1.00', '1.0'],
[1, 2, '0.5'], [1, 2, '0.5'],
[1, 3, null], [1, 3, RoundingNecessaryException::class],
[1, 4, '0.25'], [1, 4, '0.25'],
[1, 5, '0.2'], [1, 5, '0.2'],
[1, 6, null], [1, 6, RoundingNecessaryException::class],
[1, 7, null], [1, 7, RoundingNecessaryException::class],
[1, 8, '0.125'], [1, 8, '0.125'],
[1, 9, null], [1, 9, RoundingNecessaryException::class],
[1, 10, '0.1'], [1, 10, '0.1'],
['1.0', 2, '0.5'], ['1.0', 2, '0.5'],
['1.00', 2, '0.50'], ['1.00', 2, '0.50'],
@ -675,6 +679,8 @@ class BigDecimalTest extends AbstractTestCase
['1234.5678', '4', '308.64195'], ['1234.5678', '4', '308.64195'],
['1234.5678', '8', '154.320975'], ['1234.5678', '8', '154.320975'],
['1234.5678', '6.4', '192.90121875'], ['1234.5678', '6.4', '192.90121875'],
['123', '0', DivisionByZeroException::class],
[-789, '0.0', DivisionByZeroException::class],
]; ];
} }