20 private $fractionalPart;
25 private static $numbers = [0 => 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1, 7 => 1, 8 => 1, 9 => 1];
31 public function __construct($integerPart, $fractionalPart =
'')
33 if (
'' === $integerPart &&
'' === $fractionalPart) {
34 throw new \InvalidArgumentException(
'Empty number is invalid');
37 $this->integerPart = $this->parseIntegerPart((
string) $integerPart);
38 $this->fractionalPart = $this->parseFractionalPart((
string) $fractionalPart);
46 public static function fromString($number)
48 $decimalSeparatorPosition = strpos($number,
'.');
49 if ($decimalSeparatorPosition ===
false) {
50 return new self($number,
'');
54 substr($number, 0, $decimalSeparatorPosition),
55 rtrim(substr($number, $decimalSeparatorPosition + 1),
'0')
64 public static function fromFloat($number)
66 if (is_float($number) ===
false) {
67 throw new \InvalidArgumentException(
'Floating point value expected');
70 return self::fromString(sprintf(
'%.14F', $number));
78 public static function fromNumber($number)
80 if (is_float($number)) {
81 return self::fromString(sprintf(
'%.14F', $number));
84 if (is_int($number)) {
85 return new self($number);
88 if (is_string($number)) {
89 return self::fromString($number);
92 throw new \InvalidArgumentException(
'Valid numeric value expected');
98 public function isDecimal()
100 return $this->fractionalPart !==
'';
106 public function isInteger()
108 return $this->fractionalPart ===
'';
114 public function isHalf()
116 return $this->fractionalPart ===
'5';
122 public function isCurrentEven()
124 $lastIntegerPartNumber = $this->integerPart[strlen($this->integerPart) - 1];
126 return $lastIntegerPartNumber % 2 === 0;
132 public function isCloserToNext()
134 if ($this->fractionalPart ===
'') {
138 return $this->fractionalPart[0] >= 5;
144 public function __toString()
146 if ($this->fractionalPart ===
'') {
147 return $this->integerPart;
150 return $this->integerPart.
'.'.$this->fractionalPart;
156 public function isNegative()
158 return $this->integerPart[0] ===
'-';
164 public function getIntegerPart()
166 return $this->integerPart;
172 public function getFractionalPart()
174 return $this->fractionalPart;
180 public function getIntegerRoundingMultiplier()
182 if ($this->integerPart[0] ===
'-') {
194 public function base10($number)
196 if (!is_int($number)) {
197 throw new \InvalidArgumentException(
'Expecting integer');
200 if ($this->integerPart ===
'0' && !$this->fractionalPart) {
205 $integerPart = $this->integerPart;
207 if ($integerPart[0] ===
'-') {
209 $integerPart = substr($integerPart, 1);
213 $integerPart = ltrim($integerPart,
'0');
214 $lengthIntegerPart = strlen($integerPart);
215 $integers = $lengthIntegerPart - min($number, $lengthIntegerPart);
216 $zeroPad = $number - min($number, $lengthIntegerPart);
219 $sign.substr($integerPart, 0, $integers),
220 rtrim(str_pad(
'', $zeroPad,
'0').substr($integerPart, $integers).$this->fractionalPart,
'0')
224 $number = abs($number);
225 $lengthFractionalPart = strlen($this->fractionalPart);
226 $fractions = $lengthFractionalPart - min($number, $lengthFractionalPart);
227 $zeroPad = $number - min($number, $lengthFractionalPart);
230 $sign.ltrim($integerPart.substr($this->fractionalPart, 0, $lengthFractionalPart - $fractions).str_pad(
'', $zeroPad,
'0'),
'0'),
231 substr($this->fractionalPart, $lengthFractionalPart - $fractions)
240 private static function parseIntegerPart($number)
242 if (
'' === $number ||
'0' === $number) {
246 if (
'-' === $number) {
252 for ($position = 0, $characters = strlen($number); $position < $characters; ++$position) {
253 $digit = $number[$position];
255 if (!isset(static::$numbers[$digit]) && !(0 === $position &&
'-' === $digit)) {
256 throw new \InvalidArgumentException(
257 sprintf(
'Invalid integer part %1$s. Invalid digit %2$s found', $number, $digit)
261 if (
false === $nonZero &&
'0' === $digit) {
262 throw new \InvalidArgumentException(
263 'Leading zeros are not allowed'
278 private static function parseFractionalPart($number)
280 if (
'' === $number) {
284 for ($position = 0, $characters = strlen($number); $position < $characters; ++$position) {
285 $digit = $number[$position];
286 if (!isset(static::$numbers[$digit])) {
287 throw new \InvalidArgumentException(
288 sprintf(
'Invalid fractional part %1$s. Invalid digit %2$s found', $number, $digit)
303 public static function roundMoneyValue($moneyValue, $targetDigits, $havingDigits)
305 $valueLength = strlen($moneyValue);
306 $shouldRound = $targetDigits < $havingDigits && $valueLength - $havingDigits + $targetDigits > 0;
308 if ($shouldRound && $moneyValue[$valueLength - $havingDigits + $targetDigits] >= 5) {
309 $position = $valueLength - $havingDigits + $targetDigits;
312 while ($position > 0) {
313 $newValue = (string) ((
int) $moneyValue[$position - 1] + $addend);
315 if ($newValue >= 10) {
316 $moneyValue[$position - 1] = $newValue[1];
317 $addend = $newValue[0];
319 if ($position === 0) {
320 $moneyValue = $addend.$moneyValue;
323 if ($moneyValue[$position - 1] ===
'-') {
324 $moneyValue[$position - 1] = $newValue[0];
325 $moneyValue =
'-'.$moneyValue;
327 $moneyValue[$position - 1] = $newValue[0];