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:
parent
5ccc6641b5
commit
1420202440
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user