Skip to content

Commit

Permalink
Add support for exponential notation (#5)
Browse files Browse the repository at this point in the history
Add support for exponential notation
  • Loading branch information
Pablo Borowicz authored Jul 17, 2019
2 parents 42e9bc8 + 35e437c commit 11e0f87
Show file tree
Hide file tree
Showing 4 changed files with 198 additions and 98 deletions.
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ php:
- 5.6
- 7.0
- 7.1
- 7.2
- 7.3

before_script:
- travis_retry composer install --no-interaction
Expand Down
101 changes: 101 additions & 0 deletions src/Builder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php
/**
* This file is part of the PrestaShop\Decimal package
*
* @author PrestaShop SA <contact@prestashop.com>
* @license https://opensource.org/licenses/MIT MIT License
*/

namespace PrestaShop\Decimal;

use PrestaShop\Decimal\Number;


/**
* Builds Number instances
*/
class Builder
{

/**
* Pattern for most numbers
*/
const NUMBER_PATTERN = "/^(?<sign>[-+])?(?<integerPart>\d+)?(?:\.(?<fractionalPart>\d+)(?<exponentPart>[eE](?<exponentSign>[-+])(?<exponent>\d+))?)?$/";

/**
* Pattern for integer numbers in scientific notation (rare but supported by spec)
*/
const INT_EXPONENTIAL_PATTERN = "/^(?<sign>[-+])?(?<integerPart>\d+)(?<exponentPart>[eE](?<exponentSign>[-+])(?<exponent>\d+))$/";

/**
* Builds a Number from a string
*
* @param string $number
*
* @return Number
*/
public static function parseNumber($number)
{
if (!self::itLooksLikeANumber($number, $numberParts)) {
throw new \InvalidArgumentException(
sprintf('"%s" cannot be interpreted as a number', print_r($number, true))
);
}

$integerPart = '';
if (array_key_exists('integerPart', $numberParts)) {
// extract the integer part and remove leading zeroes
$integerPart = ltrim($numberParts['integerPart'], '0');
}

$fractionalPart = '';
if (array_key_exists('fractionalPart', $numberParts)) {
// extract the fractional part and remove trailing zeroes
$fractionalPart = rtrim($numberParts['fractionalPart'], '0');
}

$fractionalDigits = strlen($fractionalPart);
$coefficient = $integerPart . $fractionalPart;

// when coefficient is '0' or a sequence of '0'
if ('' === $coefficient) {
$coefficient = '0';
}

// when the number has been provided in scientific notation
if (array_key_exists('exponentPart', $numberParts)) {
$givenExponent = (int) ($numberParts['exponentSign'] . $numberParts['exponent']);

// we simply add or subtract fractional digits from the given exponent (depending if it's positive or negative)
$fractionalDigits -= $givenExponent;
if ($fractionalDigits < 0) {
// if the resulting fractional digits is negative, it means there is no fractional part anymore
// we need to add trailing zeroes as needed
$coefficient = str_pad($coefficient, strlen($coefficient) - $fractionalDigits, '0');

// there's no fractional part anymore
$fractionalDigits = 0;
}
}

return new Number($numberParts['sign'] . $coefficient, $fractionalDigits);
}

/**
* @param string $number
* @param array $numberParts
*
* @return bool
*/
private static function itLooksLikeANumber($number, &$numberParts)
{
return (
strlen((string) $number) > 0
&& (
preg_match(self::NUMBER_PATTERN, $number, $numberParts)
|| preg_match(self::INT_EXPONENTIAL_PATTERN, $number, $numberParts)
)
);
}

}
47 changes: 8 additions & 39 deletions src/Number.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ class Number
* (string) new Number('123456', 6); // -> '0.123456'
* ```
*
* Note: exponents are always positive.
* Note: decimal positions must always be a positive number.
*
* @param string $number Number or coefficient
* @param int $exponent [default=null] If provided, the number is considered a coefficient of
* the scientific notation.
* @param int $exponent [default=null] If provided, the number can be considered as the negative
* exponent of the scientific notation, or the number of fractional digits.
*/
public function __construct($number, $exponent = null)
{
Expand All @@ -69,11 +69,13 @@ public function __construct($number, $exponent = null)
}

if (null === $exponent) {
$this->initFromString($number);
} else {
$this->initFromScientificNotation($number, $exponent);
$decimalNumber = Builder::parseNumber($number);
$number = $decimalNumber->getSign() . $decimalNumber->getCoefficient();
$exponent = $decimalNumber->getExponent();
}

$this->initFromScientificNotation($number, $exponent);

if ('0' === $this->coefficient) {
// make sure the sign is always positive for zero
$this->isNegative = false;
Expand Down Expand Up @@ -455,39 +457,6 @@ public function toMagnitude($exponent)
return (new Operation\MagnitudeChange())->compute($this, $exponent);
}

/**
* Initializes the number using a string
*
* @param string $number
*/
private function initFromString($number)
{
if (!preg_match("/^(?<sign>[-+])?(?<integerPart>\d+)(?:\.(?<fractionalPart>\d+))?$/", $number, $parts)) {
throw new \InvalidArgumentException(
sprintf('"%s" cannot be interpreted as a number', print_r($number, true))
);
}

$this->isNegative = ('-' === $parts['sign']);

// extract the integer part and remove leading zeroes and plus sign
$integerPart = ltrim($parts['integerPart'], '0');

$fractionalPart = '';
if (array_key_exists('fractionalPart', $parts)) {
// extract the fractional part and remove trailing zeroes
$fractionalPart = rtrim($parts['fractionalPart'], '0');
}

$this->exponent = strlen($fractionalPart);
$this->coefficient = $integerPart . $fractionalPart;

// when coefficient is '0' or a sequence of '0'
if ('' === $this->coefficient) {
$this->coefficient = '0';
}
}

/**
* Initializes the number using a coefficient and exponent
*
Expand Down
Loading

0 comments on commit 11e0f87

Please sign in to comment.