Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
57 changes: 52 additions & 5 deletions brick/math/src/BigDecimal.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NegativeNumberException;
use Brick\Math\Internal\Calculator;
use Override;

/**
* Immutable, arbitrary-precision signed decimal numbers.
Expand Down Expand Up @@ -47,6 +48,7 @@ protected function __construct(string $value, int $scale = 0)
/**
* @psalm-pure
*/
#[Override]
protected static function from(BigNumber $number): static
{
return $number->toBigDecimal();
Expand All @@ -58,19 +60,23 @@ protected static function from(BigNumber $number): static
* Example: `(12345, 3)` will result in the BigDecimal `12.345`.
*
* @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
* @param int $scale The scale of the number, positive or zero.
*
* @throws \InvalidArgumentException If the scale is negative.
* @param int $scale The scale of the number. If negative, the scale will be set to zero
* and the unscaled value will be adjusted accordingly.
*
* @psalm-pure
*/
public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal
{
$value = (string) BigInteger::of($value);

if ($scale < 0) {
throw new \InvalidArgumentException('The scale cannot be negative.');
if ($value !== '0') {
$value .= \str_repeat('0', -$scale);
}
$scale = 0;
}

return new BigDecimal((string) BigInteger::of($value), $scale);
return new BigDecimal($value, $scale);
}

/**
Expand Down Expand Up @@ -535,6 +541,7 @@ public function negated() : BigDecimal
return new BigDecimal(Calculator::get()->neg($this->value), $this->scale);
}

#[Override]
public function compareTo(BigNumber|int|float|string $that) : int
{
$that = BigNumber::of($that);
Expand All @@ -552,6 +559,7 @@ public function compareTo(BigNumber|int|float|string $that) : int
return - $that->compareTo($this);
}

#[Override]
public function getSign() : int
{
return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
Expand All @@ -567,6 +575,33 @@ public function getScale() : int
return $this->scale;
}

/**
* Returns the number of significant digits in the number.
*
* This is the number of digits to both sides of the decimal point, stripped of leading zeros.
* The sign has no impact on the result.
*
* Examples:
* 0 => 0
* 0.0 => 0
* 123 => 3
* 123.456 => 6
* 0.00123 => 3
* 0.0012300 => 5
*/
public function getPrecision(): int
{
$value = $this->value;

if ($value === '0') {
return 0;
}

$length = \strlen($value);

return ($value[0] === '-') ? $length - 1 : $length;
}

/**
* Returns a string representing the integral part of this decimal number.
*
Expand Down Expand Up @@ -609,18 +644,21 @@ public function hasNonZeroFractionalPart() : bool
return $this->getFractionalPart() !== \str_repeat('0', $this->scale);
}

#[Override]
public function toBigInteger() : BigInteger
{
$zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0);

return self::newBigInteger($zeroScaleDecimal->value);
}

#[Override]
public function toBigDecimal() : BigDecimal
{
return $this;
}

#[Override]
public function toBigRational() : BigRational
{
$numerator = self::newBigInteger($this->value);
Expand All @@ -629,6 +667,7 @@ public function toBigRational() : BigRational
return self::newBigRational($numerator, $denominator, false);
}

#[Override]
public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
if ($scale === $this->scale) {
Expand All @@ -638,24 +677,32 @@ public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::U
return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode);
}

#[Override]
public function toInt() : int
{
return $this->toBigInteger()->toInt();
}

#[Override]
public function toFloat() : float
{
return (float) (string) $this;
}

/**
* @return numeric-string
*/
#[Override]
public function __toString() : string
{
if ($this->scale === 0) {
/** @var numeric-string */
return $this->value;
}

$value = $this->getUnscaledValueWithLeadingZeros();

/** @var numeric-string */
return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale);
}

Expand Down
15 changes: 15 additions & 0 deletions brick/math/src/BigInteger.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Brick\Math\Exception\NegativeNumberException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Internal\Calculator;
use Override;

/**
* An arbitrary-size integer.
Expand Down Expand Up @@ -42,6 +43,7 @@ protected function __construct(string $value)
/**
* @psalm-pure
*/
#[Override]
protected static function from(BigNumber $number): static
{
return $number->toBigInteger();
Expand Down Expand Up @@ -856,6 +858,7 @@ public function testBit(int $n) : bool
return $this->shiftedRight($n)->isOdd();
}

#[Override]
public function compareTo(BigNumber|int|float|string $that) : int
{
$that = BigNumber::of($that);
Expand All @@ -867,31 +870,37 @@ public function compareTo(BigNumber|int|float|string $that) : int
return - $that->compareTo($this);
}

#[Override]
public function getSign() : int
{
return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
}

#[Override]
public function toBigInteger() : BigInteger
{
return $this;
}

#[Override]
public function toBigDecimal() : BigDecimal
{
return self::newBigDecimal($this->value);
}

#[Override]
public function toBigRational() : BigRational
{
return self::newBigRational($this, BigInteger::one(), false);
}

#[Override]
public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
return $this->toBigDecimal()->toScale($scale, $roundingMode);
}

#[Override]
public function toInt() : int
{
$intValue = (int) $this->value;
Expand All @@ -903,6 +912,7 @@ public function toInt() : int
return $intValue;
}

#[Override]
public function toFloat() : float
{
return (float) $this->value;
Expand Down Expand Up @@ -1013,8 +1023,13 @@ public function toBytes(bool $signed = true) : string
return \hex2bin($hex);
}

/**
* @return numeric-string
*/
#[Override]
public function __toString() : string
{
/** @var numeric-string */
return $this->value;
}

Expand Down
10 changes: 8 additions & 2 deletions brick/math/src/BigNumber.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Exception\RoundingNecessaryException;
use Override;

/**
* Common interface for arbitrary-precision rational numbers.
Expand Down Expand Up @@ -51,8 +52,9 @@ abstract class BigNumber implements \JsonSerializable
* - strings containing a `.` character or using an exponential notation are returned as BigDecimal
* - strings containing only digits with an optional leading `+` or `-` sign are returned as BigInteger
*
* @throws NumberFormatException If the format of the number is not valid.
* @throws NumberFormatException If the format of the number is not valid.
* @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
* @throws RoundingNecessaryException If the value cannot be converted to an instance of the subclass without rounding.
*
* @psalm-pure
*/
Expand All @@ -71,6 +73,9 @@ final public static function of(BigNumber|int|float|string $value) : static
}

/**
* @throws NumberFormatException If the format of the number is not valid.
* @throws DivisionByZeroException If the value represents a rational number with a denominator of zero.
*
* @psalm-pure
*/
private static function _of(BigNumber|int|float|string $value) : BigNumber
Expand Down Expand Up @@ -163,7 +168,7 @@ private static function _of(BigNumber|int|float|string $value) : BigNumber
/**
* Overridden by subclasses to convert a BigNumber to an instance of the subclass.
*
* @throws MathException If the value cannot be converted.
* @throws RoundingNecessaryException If the value cannot be converted.
*
* @psalm-pure
*/
Expand Down Expand Up @@ -502,6 +507,7 @@ abstract public function toFloat() : float;
*/
abstract public function __toString() : string;

#[Override]
final public function jsonSerialize() : string
{
return $this->__toString();
Expand Down
13 changes: 12 additions & 1 deletion brick/math/src/BigRational.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Brick\Math\Exception\MathException;
use Brick\Math\Exception\NumberFormatException;
use Brick\Math\Exception\RoundingNecessaryException;
use Override;

/**
* An arbitrarily large rational number.
Expand Down Expand Up @@ -57,6 +58,7 @@ protected function __construct(BigInteger $numerator, BigInteger $denominator, b
/**
* @psalm-pure
*/
#[Override]
protected static function from(BigNumber $number): static
{
return $number->toBigRational();
Expand Down Expand Up @@ -320,16 +322,19 @@ public function simplified() : BigRational
return new BigRational($numerator, $denominator, false);
}

#[Override]
public function compareTo(BigNumber|int|float|string $that) : int
{
return $this->minus($that)->getSign();
}

#[Override]
public function getSign() : int
{
return $this->numerator->getSign();
}

#[Override]
public function toBigInteger() : BigInteger
{
$simplified = $this->simplified();
Expand All @@ -341,32 +346,38 @@ public function toBigInteger() : BigInteger
return $simplified->numerator;
}

#[Override]
public function toBigDecimal() : BigDecimal
{
return $this->numerator->toBigDecimal()->exactlyDividedBy($this->denominator);
}

#[Override]
public function toBigRational() : BigRational
{
return $this;
}

#[Override]
public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
{
return $this->numerator->toBigDecimal()->dividedBy($this->denominator, $scale, $roundingMode);
}

#[Override]
public function toInt() : int
{
return $this->toBigInteger()->toInt();
}

#[Override]
public function toFloat() : float
{
$simplified = $this->simplified();
return $simplified->numerator->toFloat() / $simplified->denominator->toFloat();
}

#[Override]
public function __toString() : string
{
$numerator = (string) $this->numerator;
Expand All @@ -376,7 +387,7 @@ public function __toString() : string
return $numerator;
}

return $this->numerator . '/' . $this->denominator;
return $numerator . '/' . $denominator;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion brick/math/src/Exception/MathException.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
/**
* Base class for all math exceptions.
*/
class MathException extends \Exception
class MathException extends \RuntimeException
{
}
Loading