1
0
mirror of https://github.com/danog/math.git synced 2024-11-30 04:19:31 +01:00

Introducing a specific DivisionByZeroException

This commit is contained in:
Benjamin Morel 2015-06-22 14:12:40 +02:00
parent 7680dcf634
commit ed8e1075e2
9 changed files with 131 additions and 76 deletions

View File

@ -3,6 +3,7 @@
namespace Brick\Math;
use Brick\Math\Exception\ArithmeticException;
use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Internal\Calculator;
/**
@ -221,6 +222,7 @@ final class BigDecimal extends BigNumber implements \Serializable
*
* @return BigDecimal
*
* @throws DivisionByZeroException If the divisor is zero.
* @throws ArithmeticException If RoundingMode::UNNECESSARY is provided and rounding was necessary.
* @throws \InvalidArgumentException If any of the arguments is not valid.
*/
@ -229,7 +231,7 @@ final class BigDecimal extends BigNumber implements \Serializable
$that = BigDecimal::of($that);
if ($that->isZero()) {
throw ArithmeticException::divisionByZero();
throw DivisionByZeroException::divisionByZero();
}
if ($scale === null) {
@ -355,14 +357,14 @@ final class BigDecimal extends BigNumber implements \Serializable
*
* @return BigDecimal[] An array containing the quotient and the remainder.
*
* @throws ArithmeticException If the divisor is zero.
* @throws DivisionByZeroException If the divisor is zero.
*/
public function divideAndRemainder($that)
{
$that = BigDecimal::of($that);
if ($that->isZero()) {
throw ArithmeticException::divisionByZero();
throw DivisionByZeroException::divisionByZero();
}
$p = $this->valueWithMinScale($that->scale);

View File

@ -3,6 +3,7 @@
namespace Brick\Math;
use Brick\Math\Exception\ArithmeticException;
use Brick\Math\Exception\DivisionByZeroException;
use Brick\Math\Internal\Calculator;
/**
@ -255,7 +256,7 @@ final class BigInteger extends BigNumber implements \Serializable
*
* @return BigInteger
*
* @throws ArithmeticException If the divisor is zero.
* @throws DivisionByZeroException If the divisor is zero.
*/
public function dividedBy($that)
{
@ -266,7 +267,7 @@ final class BigInteger extends BigNumber implements \Serializable
}
if ($that->value === '0') {
throw ArithmeticException::divisionByZero();
throw DivisionByZeroException::divisionByZero();
}
list ($quotient) = Calculator::get()->div($this->value, $that->value);
@ -281,14 +282,14 @@ final class BigInteger extends BigNumber implements \Serializable
*
* @return BigInteger[] An array containing the quotient and the remainder.
*
* @throws ArithmeticException If the divisor is zero.
* @throws DivisionByZeroException If the divisor is zero.
*/
public function divideAndRemainder($that)
{
$that = BigInteger::of($that);
if ($that->value === '0') {
throw ArithmeticException::divisionByZero();
throw DivisionByZeroException::divisionByZero();
}
list ($quotient, $remainder) = Calculator::get()->div($this->value, $that->value);

View File

@ -3,6 +3,7 @@
namespace Brick\Math;
use Brick\Math\Exception\ArithmeticException;
use Brick\Math\Exception\DivisionByZeroException;
/**
* Common interface for arbitrary-precision numbers.
@ -42,11 +43,12 @@ abstract class BigNumber
* @return static
*
* @throws \InvalidArgumentException If the number is not valid.
* @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
*/
public static function of($value)
{
try {
if ($value instanceof BigNumber) {
if ($value instanceof BigNumber) {
try {
switch (static::class) {
case BigInteger::class:
return $value->toBigInteger();
@ -60,33 +62,43 @@ abstract class BigNumber
default:
return $value;
}
} catch (ArithmeticException $e) {
$className = substr(static::class, strrpos(static::class, '\\') + 1);
throw new \InvalidArgumentException('Cannot convert value to a ' . $className . ' without losing precision.');
}
}
if (is_int($value)) {
switch (static::class) {
case BigDecimal::class:
return new BigDecimal((string) $value);
case BigRational::class:
return new BigRational(new BigInteger((string) $value), new BigInteger('1'));
default:
return new BigInteger((string) $value);
}
}
$value = (string) $value;
if (preg_match(BigNumber::REGEXP, $value, $matches) !== 1) {
throw new \InvalidArgumentException('The given value does not represent a valid number.');
}
if (isset($matches['denominator'])) {
$numerator = BigNumber::cleanUp($matches['integral']);
$denominator = ltrim($matches['denominator'], '0');
if ($denominator === '') {
throw DivisionByZeroException::denominatorMustNotBeZero();
}
if (is_int($value)) {
switch (static::class) {
case BigDecimal::class:
return new BigDecimal((string) $value);
case BigRational::class:
return new BigRational(new BigInteger((string) $value), new BigInteger('1'));
default:
return new BigInteger((string) $value);
}
}
$value = (string) $value;
if (preg_match(BigNumber::REGEXP, $value, $matches) !== 1) {
throw new \InvalidArgumentException('The given value does not represent a valid number.');
}
if (isset($matches['denominator'])) {
$numerator = BigNumber::cleanUp($matches['integral']);
$denominator = BigNumber::cleanUp($matches['denominator']); // @todo doesn't need sign check
$result = new BigRational(new BigInteger($numerator), new BigInteger($denominator), true);
$result = new BigRational(new BigInteger($numerator), new BigInteger($denominator), false);
try {
switch (static::class) {
case BigInteger::class:
return $result->toBigInteger();
@ -97,23 +109,29 @@ abstract class BigNumber
default:
return $result;
}
} elseif (isset($matches['fractional']) || isset($matches['exponent'])) {
$fractional = isset($matches['fractional']) ? $matches['fractional'] : '';
$exponent = isset($matches['exponent']) ? (int) $matches['exponent'] : 0;
} catch (ArithmeticException $e) {
$className = substr(static::class, strrpos(static::class, '\\') + 1);
$unscaledValue = BigNumber::cleanUp($matches['integral'] . $fractional);
throw new \InvalidArgumentException('Cannot convert value to a ' . $className . ' without losing precision.');
}
} elseif (isset($matches['fractional']) || isset($matches['exponent'])) {
$fractional = isset($matches['fractional']) ? $matches['fractional'] : '';
$exponent = isset($matches['exponent']) ? (int) $matches['exponent'] : 0;
$scale = strlen($fractional) - $exponent;
$unscaledValue = BigNumber::cleanUp($matches['integral'] . $fractional);
if ($scale < 0) {
if ($unscaledValue !== '0') {
$unscaledValue .= str_repeat('0', - $scale);
}
$scale = 0;
$scale = strlen($fractional) - $exponent;
if ($scale < 0) {
if ($unscaledValue !== '0') {
$unscaledValue .= str_repeat('0', - $scale);
}
$scale = 0;
}
$result = new BigDecimal($unscaledValue, $scale);
$result = new BigDecimal($unscaledValue, $scale);
try {
switch (static::class) {
case BigInteger::class:
return $result->toBigInteger();
@ -124,24 +142,24 @@ abstract class BigNumber
default:
return $result;
}
} else {
$integral = BigNumber::cleanUp($matches['integral']);
} catch (ArithmeticException $e) {
$className = substr(static::class, strrpos(static::class, '\\') + 1);
switch (static::class) {
case BigDecimal::class:
return new BigDecimal($integral);
case BigRational::class:
return new BigRational(new BigInteger($integral), new BigInteger('1'), false);
default:
return new BigInteger($integral);
}
throw new \InvalidArgumentException('Cannot convert value to a ' . $className . ' without losing precision.');
}
} catch (ArithmeticException $e) {
$className = substr(static::class, strrpos(static::class, '\\') + 1);
} else {
$integral = BigNumber::cleanUp($matches['integral']);
throw new \InvalidArgumentException('Cannot convert value to a ' . $className . ' without losing precision.');
switch (static::class) {
case BigDecimal::class:
return new BigDecimal($integral);
case BigRational::class:
return new BigRational(new BigInteger($integral), new BigInteger('1'), false);
default:
return new BigInteger($integral);
}
}
}

View File

@ -3,6 +3,7 @@
namespace Brick\Math;
use Brick\Math\Exception\ArithmeticException;
use Brick\Math\Exception\DivisionByZeroException;
/**
* An arbitrarily large rational number.
@ -32,13 +33,13 @@ final class BigRational extends BigNumber implements \Serializable
* @param BigInteger $denominator The denominator.
* @param bool $checkDemominator Whether to check the denominator for negative and zero.
*
* @throws ArithmeticException If the denominator is zero.
* @throws DivisionByZeroException If the denominator is zero.
*/
protected function __construct(BigInteger $numerator, BigInteger $denominator, $checkDemominator)
{
if ($checkDemominator) {
if ($denominator->isZero()) {
throw new ArithmeticException('The denominator must not be zero.');
throw DivisionByZeroException::denominatorMustNotBeZero();
}
if ($denominator->isNegative()) {
@ -62,7 +63,7 @@ final class BigRational extends BigNumber implements \Serializable
*
* @return BigRational
*
* @throws ArithmeticException If the denominator is zero.
* @throws DivisionByZeroException If the denominator is zero.
*/
public static function nd($numerator, $denominator)
{
@ -181,7 +182,7 @@ final class BigRational extends BigNumber implements \Serializable
*
* @return BigRational
*
* @throws ArithmeticException If the numerator is zero.
* @throws DivisionByZeroException If the numerator is zero.
*/
public function reciprocal()
{

View File

@ -21,14 +21,6 @@ class ArithmeticException extends \RuntimeException
return new self(sprintf($message, (string) $value, ~PHP_INT_MAX, PHP_INT_MAX));
}
/**
* @return ArithmeticException
*/
public static function divisionByZero()
{
return new self('Division by zero.');
}
/**
* @return ArithmeticException
*/

View File

@ -0,0 +1,25 @@
<?php
namespace Brick\Math\Exception;
/**
* Exception thrown when a division by zero occurs.
*/
class DivisionByZeroException extends ArithmeticException
{
/**
* @return ArithmeticException
*/
public static function divisionByZero()
{
return new self('Division by zero.');
}
/**
* @return DivisionByZeroException
*/
public static function denominatorMustNotBeZero()
{
return new DivisionByZeroException('The denominator of a rational number cannot be zero.');
}
}

View File

@ -583,7 +583,7 @@ class BigDecimalTest extends AbstractTestCase
/**
* @dataProvider providerDividedByZeroThrowsException
* @expectedException \Brick\Math\Exception\ArithmeticException
* @expectedException \Brick\Math\Exception\DivisionByZeroException
*
* @param string|number $zero
*/
@ -1289,7 +1289,7 @@ class BigDecimalTest extends AbstractTestCase
}
/**
* @expectedException \Brick\Math\Exception\ArithmeticException
* @expectedException \Brick\Math\Exception\DivisionByZeroException
*/
public function testDivideAndRemainderByZeroThrowsException()
{

View File

@ -505,7 +505,7 @@ class BigIntegerTest extends AbstractTestCase
}
/**
* @expectedException \Brick\Math\Exception\ArithmeticException
* @expectedException \Brick\Math\Exception\DivisionByZeroException
*/
public function testDividedByZeroThrowsException()
{
@ -583,7 +583,7 @@ class BigIntegerTest extends AbstractTestCase
}
/**
* @expectedException \Brick\Math\Exception\ArithmeticException
* @expectedException \Brick\Math\Exception\DivisionByZeroException
*/
public function testDivideAndRemainderByZeroThrowsException()
{

View File

@ -39,6 +39,14 @@ class BigRationalTest extends AbstractTestCase
];
}
/**
* @expectedException \Brick\Math\Exception\DivisionByZeroException
*/
public function testNdWithZeroDenominator()
{
BigRational::nd(1, 0);
}
/**
* @dataProvider providerOf
*
@ -69,6 +77,14 @@ class BigRationalTest extends AbstractTestCase
];
}
/**
* @expectedException \Brick\Math\Exception\DivisionByZeroException
*/
public function testOfWithZeroDenominator()
{
BigRational::of('2/0');
}
/**
* @dataProvider providerParseInvalidString
* @expectedException \InvalidArgumentException
@ -246,7 +262,7 @@ class BigRationalTest extends AbstractTestCase
}
/**
* @expectedException \Brick\Math\Exception\ArithmeticException
* @expectedException \Brick\Math\Exception\DivisionByZeroException
*/
public function testReciprocalOfZeroThrowsException()
{