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);
$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) {
$result = $result->withScale($this->scale);

View File

@ -271,37 +271,7 @@ final class BigRational extends BigNumber implements \Serializable
*/
public function toBigDecimal()
{
$simplified = $this->simplified();
$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();
return $this->numerator->toBigDecimal()->dividedBy($this->denominator);
}
/**

View File

@ -3,6 +3,7 @@
namespace Brick\Math\Tests;
use Brick\Math\BigDecimal;
use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\RoundingMode;
use Brick\Math\Exception\RoundingNecessaryException;
@ -633,17 +634,20 @@ class BigDecimalTest extends AbstractTestCase
*
* @param string|number $number The number to divide.
* @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)
{
if ($expected === null) {
$this->setExpectedException(RoundingNecessaryException::class);
$number = BigDecimal::of($number);
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);
}
}
@ -657,13 +661,13 @@ class BigDecimalTest extends AbstractTestCase
[1, 1, '1'],
['1.0', '1.00', '1.0'],
[1, 2, '0.5'],
[1, 3, null],
[1, 3, RoundingNecessaryException::class],
[1, 4, '0.25'],
[1, 5, '0.2'],
[1, 6, null],
[1, 7, null],
[1, 6, RoundingNecessaryException::class],
[1, 7, RoundingNecessaryException::class],
[1, 8, '0.125'],
[1, 9, null],
[1, 9, RoundingNecessaryException::class],
[1, 10, '0.1'],
['1.0', 2, '0.5'],
['1.00', 2, '0.50'],
@ -675,6 +679,8 @@ class BigDecimalTest extends AbstractTestCase
['1234.5678', '4', '308.64195'],
['1234.5678', '8', '154.320975'],
['1234.5678', '6.4', '192.90121875'],
['123', '0', DivisionByZeroException::class],
[-789, '0.0', DivisionByZeroException::class],
];
}