BigDecimal.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  1. <?php
  2. declare(strict_types=1);
  3. namespace Brick\Math;
  4. use Brick\Math\Exception\DivisionByZeroException;
  5. use Brick\Math\Exception\MathException;
  6. use Brick\Math\Exception\NegativeNumberException;
  7. use Brick\Math\Internal\Calculator;
  8. use Override;
  9. /**
  10. * Immutable, arbitrary-precision signed decimal numbers.
  11. *
  12. * @psalm-immutable
  13. */
  14. final class BigDecimal extends BigNumber
  15. {
  16. /**
  17. * The unscaled value of this decimal number.
  18. *
  19. * This is a string of digits with an optional leading minus sign.
  20. * No leading zero must be present.
  21. * No leading minus sign must be present if the value is 0.
  22. */
  23. private readonly string $value;
  24. /**
  25. * The scale (number of digits after the decimal point) of this decimal number.
  26. *
  27. * This must be zero or more.
  28. */
  29. private readonly int $scale;
  30. /**
  31. * Protected constructor. Use a factory method to obtain an instance.
  32. *
  33. * @param string $value The unscaled value, validated.
  34. * @param int $scale The scale, validated.
  35. */
  36. protected function __construct(string $value, int $scale = 0)
  37. {
  38. $this->value = $value;
  39. $this->scale = $scale;
  40. }
  41. /**
  42. * @psalm-pure
  43. */
  44. #[Override]
  45. protected static function from(BigNumber $number): static
  46. {
  47. return $number->toBigDecimal();
  48. }
  49. /**
  50. * Creates a BigDecimal from an unscaled value and a scale.
  51. *
  52. * Example: `(12345, 3)` will result in the BigDecimal `12.345`.
  53. *
  54. * @param BigNumber|int|float|string $value The unscaled value. Must be convertible to a BigInteger.
  55. * @param int $scale The scale of the number, positive or zero.
  56. *
  57. * @throws \InvalidArgumentException If the scale is negative.
  58. *
  59. * @psalm-pure
  60. */
  61. public static function ofUnscaledValue(BigNumber|int|float|string $value, int $scale = 0) : BigDecimal
  62. {
  63. if ($scale < 0) {
  64. throw new \InvalidArgumentException('The scale cannot be negative.');
  65. }
  66. return new BigDecimal((string) BigInteger::of($value), $scale);
  67. }
  68. /**
  69. * Returns a BigDecimal representing zero, with a scale of zero.
  70. *
  71. * @psalm-pure
  72. */
  73. public static function zero() : BigDecimal
  74. {
  75. /**
  76. * @psalm-suppress ImpureStaticVariable
  77. * @var BigDecimal|null $zero
  78. */
  79. static $zero;
  80. if ($zero === null) {
  81. $zero = new BigDecimal('0');
  82. }
  83. return $zero;
  84. }
  85. /**
  86. * Returns a BigDecimal representing one, with a scale of zero.
  87. *
  88. * @psalm-pure
  89. */
  90. public static function one() : BigDecimal
  91. {
  92. /**
  93. * @psalm-suppress ImpureStaticVariable
  94. * @var BigDecimal|null $one
  95. */
  96. static $one;
  97. if ($one === null) {
  98. $one = new BigDecimal('1');
  99. }
  100. return $one;
  101. }
  102. /**
  103. * Returns a BigDecimal representing ten, with a scale of zero.
  104. *
  105. * @psalm-pure
  106. */
  107. public static function ten() : BigDecimal
  108. {
  109. /**
  110. * @psalm-suppress ImpureStaticVariable
  111. * @var BigDecimal|null $ten
  112. */
  113. static $ten;
  114. if ($ten === null) {
  115. $ten = new BigDecimal('10');
  116. }
  117. return $ten;
  118. }
  119. /**
  120. * Returns the sum of this number and the given one.
  121. *
  122. * The result has a scale of `max($this->scale, $that->scale)`.
  123. *
  124. * @param BigNumber|int|float|string $that The number to add. Must be convertible to a BigDecimal.
  125. *
  126. * @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
  127. */
  128. public function plus(BigNumber|int|float|string $that) : BigDecimal
  129. {
  130. $that = BigDecimal::of($that);
  131. if ($that->value === '0' && $that->scale <= $this->scale) {
  132. return $this;
  133. }
  134. if ($this->value === '0' && $this->scale <= $that->scale) {
  135. return $that;
  136. }
  137. [$a, $b] = $this->scaleValues($this, $that);
  138. $value = Calculator::get()->add($a, $b);
  139. $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
  140. return new BigDecimal($value, $scale);
  141. }
  142. /**
  143. * Returns the difference of this number and the given one.
  144. *
  145. * The result has a scale of `max($this->scale, $that->scale)`.
  146. *
  147. * @param BigNumber|int|float|string $that The number to subtract. Must be convertible to a BigDecimal.
  148. *
  149. * @throws MathException If the number is not valid, or is not convertible to a BigDecimal.
  150. */
  151. public function minus(BigNumber|int|float|string $that) : BigDecimal
  152. {
  153. $that = BigDecimal::of($that);
  154. if ($that->value === '0' && $that->scale <= $this->scale) {
  155. return $this;
  156. }
  157. [$a, $b] = $this->scaleValues($this, $that);
  158. $value = Calculator::get()->sub($a, $b);
  159. $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
  160. return new BigDecimal($value, $scale);
  161. }
  162. /**
  163. * Returns the product of this number and the given one.
  164. *
  165. * The result has a scale of `$this->scale + $that->scale`.
  166. *
  167. * @param BigNumber|int|float|string $that The multiplier. Must be convertible to a BigDecimal.
  168. *
  169. * @throws MathException If the multiplier is not a valid number, or is not convertible to a BigDecimal.
  170. */
  171. public function multipliedBy(BigNumber|int|float|string $that) : BigDecimal
  172. {
  173. $that = BigDecimal::of($that);
  174. if ($that->value === '1' && $that->scale === 0) {
  175. return $this;
  176. }
  177. if ($this->value === '1' && $this->scale === 0) {
  178. return $that;
  179. }
  180. $value = Calculator::get()->mul($this->value, $that->value);
  181. $scale = $this->scale + $that->scale;
  182. return new BigDecimal($value, $scale);
  183. }
  184. /**
  185. * Returns the result of the division of this number by the given one, at the given scale.
  186. *
  187. * @param BigNumber|int|float|string $that The divisor.
  188. * @param int|null $scale The desired scale, or null to use the scale of this number.
  189. * @param RoundingMode $roundingMode An optional rounding mode, defaults to UNNECESSARY.
  190. *
  191. * @throws \InvalidArgumentException If the scale or rounding mode is invalid.
  192. * @throws MathException If the number is invalid, is zero, or rounding was necessary.
  193. */
  194. public function dividedBy(BigNumber|int|float|string $that, ?int $scale = null, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
  195. {
  196. $that = BigDecimal::of($that);
  197. if ($that->isZero()) {
  198. throw DivisionByZeroException::divisionByZero();
  199. }
  200. if ($scale === null) {
  201. $scale = $this->scale;
  202. } elseif ($scale < 0) {
  203. throw new \InvalidArgumentException('Scale cannot be negative.');
  204. }
  205. if ($that->value === '1' && $that->scale === 0 && $scale === $this->scale) {
  206. return $this;
  207. }
  208. $p = $this->valueWithMinScale($that->scale + $scale);
  209. $q = $that->valueWithMinScale($this->scale - $scale);
  210. $result = Calculator::get()->divRound($p, $q, $roundingMode);
  211. return new BigDecimal($result, $scale);
  212. }
  213. /**
  214. * Returns the exact result of the division of this number by the given one.
  215. *
  216. * The scale of the result is automatically calculated to fit all the fraction digits.
  217. *
  218. * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
  219. *
  220. * @throws MathException If the divisor is not a valid number, is not convertible to a BigDecimal, is zero,
  221. * or the result yields an infinite number of digits.
  222. */
  223. public function exactlyDividedBy(BigNumber|int|float|string $that) : BigDecimal
  224. {
  225. $that = BigDecimal::of($that);
  226. if ($that->value === '0') {
  227. throw DivisionByZeroException::divisionByZero();
  228. }
  229. [, $b] = $this->scaleValues($this, $that);
  230. $d = \rtrim($b, '0');
  231. $scale = \strlen($b) - \strlen($d);
  232. $calculator = Calculator::get();
  233. foreach ([5, 2] as $prime) {
  234. for (;;) {
  235. $lastDigit = (int) $d[-1];
  236. if ($lastDigit % $prime !== 0) {
  237. break;
  238. }
  239. $d = $calculator->divQ($d, (string) $prime);
  240. $scale++;
  241. }
  242. }
  243. return $this->dividedBy($that, $scale)->stripTrailingZeros();
  244. }
  245. /**
  246. * Returns this number exponentiated to the given value.
  247. *
  248. * The result has a scale of `$this->scale * $exponent`.
  249. *
  250. * @throws \InvalidArgumentException If the exponent is not in the range 0 to 1,000,000.
  251. */
  252. public function power(int $exponent) : BigDecimal
  253. {
  254. if ($exponent === 0) {
  255. return BigDecimal::one();
  256. }
  257. if ($exponent === 1) {
  258. return $this;
  259. }
  260. if ($exponent < 0 || $exponent > Calculator::MAX_POWER) {
  261. throw new \InvalidArgumentException(\sprintf(
  262. 'The exponent %d is not in the range 0 to %d.',
  263. $exponent,
  264. Calculator::MAX_POWER
  265. ));
  266. }
  267. return new BigDecimal(Calculator::get()->pow($this->value, $exponent), $this->scale * $exponent);
  268. }
  269. /**
  270. * Returns the quotient of the division of this number by the given one.
  271. *
  272. * The quotient has a scale of `0`.
  273. *
  274. * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
  275. *
  276. * @throws MathException If the divisor is not a valid decimal number, or is zero.
  277. */
  278. public function quotient(BigNumber|int|float|string $that) : BigDecimal
  279. {
  280. $that = BigDecimal::of($that);
  281. if ($that->isZero()) {
  282. throw DivisionByZeroException::divisionByZero();
  283. }
  284. $p = $this->valueWithMinScale($that->scale);
  285. $q = $that->valueWithMinScale($this->scale);
  286. $quotient = Calculator::get()->divQ($p, $q);
  287. return new BigDecimal($quotient, 0);
  288. }
  289. /**
  290. * Returns the remainder of the division of this number by the given one.
  291. *
  292. * The remainder has a scale of `max($this->scale, $that->scale)`.
  293. *
  294. * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
  295. *
  296. * @throws MathException If the divisor is not a valid decimal number, or is zero.
  297. */
  298. public function remainder(BigNumber|int|float|string $that) : BigDecimal
  299. {
  300. $that = BigDecimal::of($that);
  301. if ($that->isZero()) {
  302. throw DivisionByZeroException::divisionByZero();
  303. }
  304. $p = $this->valueWithMinScale($that->scale);
  305. $q = $that->valueWithMinScale($this->scale);
  306. $remainder = Calculator::get()->divR($p, $q);
  307. $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
  308. return new BigDecimal($remainder, $scale);
  309. }
  310. /**
  311. * Returns the quotient and remainder of the division of this number by the given one.
  312. *
  313. * The quotient has a scale of `0`, and the remainder has a scale of `max($this->scale, $that->scale)`.
  314. *
  315. * @param BigNumber|int|float|string $that The divisor. Must be convertible to a BigDecimal.
  316. *
  317. * @return BigDecimal[] An array containing the quotient and the remainder.
  318. *
  319. * @psalm-return array{BigDecimal, BigDecimal}
  320. *
  321. * @throws MathException If the divisor is not a valid decimal number, or is zero.
  322. */
  323. public function quotientAndRemainder(BigNumber|int|float|string $that) : array
  324. {
  325. $that = BigDecimal::of($that);
  326. if ($that->isZero()) {
  327. throw DivisionByZeroException::divisionByZero();
  328. }
  329. $p = $this->valueWithMinScale($that->scale);
  330. $q = $that->valueWithMinScale($this->scale);
  331. [$quotient, $remainder] = Calculator::get()->divQR($p, $q);
  332. $scale = $this->scale > $that->scale ? $this->scale : $that->scale;
  333. $quotient = new BigDecimal($quotient, 0);
  334. $remainder = new BigDecimal($remainder, $scale);
  335. return [$quotient, $remainder];
  336. }
  337. /**
  338. * Returns the square root of this number, rounded down to the given number of decimals.
  339. *
  340. * @throws \InvalidArgumentException If the scale is negative.
  341. * @throws NegativeNumberException If this number is negative.
  342. */
  343. public function sqrt(int $scale) : BigDecimal
  344. {
  345. if ($scale < 0) {
  346. throw new \InvalidArgumentException('Scale cannot be negative.');
  347. }
  348. if ($this->value === '0') {
  349. return new BigDecimal('0', $scale);
  350. }
  351. if ($this->value[0] === '-') {
  352. throw new NegativeNumberException('Cannot calculate the square root of a negative number.');
  353. }
  354. $value = $this->value;
  355. $addDigits = 2 * $scale - $this->scale;
  356. if ($addDigits > 0) {
  357. // add zeros
  358. $value .= \str_repeat('0', $addDigits);
  359. } elseif ($addDigits < 0) {
  360. // trim digits
  361. if (-$addDigits >= \strlen($this->value)) {
  362. // requesting a scale too low, will always yield a zero result
  363. return new BigDecimal('0', $scale);
  364. }
  365. $value = \substr($value, 0, $addDigits);
  366. }
  367. $value = Calculator::get()->sqrt($value);
  368. return new BigDecimal($value, $scale);
  369. }
  370. /**
  371. * Returns a copy of this BigDecimal with the decimal point moved $n places to the left.
  372. */
  373. public function withPointMovedLeft(int $n) : BigDecimal
  374. {
  375. if ($n === 0) {
  376. return $this;
  377. }
  378. if ($n < 0) {
  379. return $this->withPointMovedRight(-$n);
  380. }
  381. return new BigDecimal($this->value, $this->scale + $n);
  382. }
  383. /**
  384. * Returns a copy of this BigDecimal with the decimal point moved $n places to the right.
  385. */
  386. public function withPointMovedRight(int $n) : BigDecimal
  387. {
  388. if ($n === 0) {
  389. return $this;
  390. }
  391. if ($n < 0) {
  392. return $this->withPointMovedLeft(-$n);
  393. }
  394. $value = $this->value;
  395. $scale = $this->scale - $n;
  396. if ($scale < 0) {
  397. if ($value !== '0') {
  398. $value .= \str_repeat('0', -$scale);
  399. }
  400. $scale = 0;
  401. }
  402. return new BigDecimal($value, $scale);
  403. }
  404. /**
  405. * Returns a copy of this BigDecimal with any trailing zeros removed from the fractional part.
  406. */
  407. public function stripTrailingZeros() : BigDecimal
  408. {
  409. if ($this->scale === 0) {
  410. return $this;
  411. }
  412. $trimmedValue = \rtrim($this->value, '0');
  413. if ($trimmedValue === '') {
  414. return BigDecimal::zero();
  415. }
  416. $trimmableZeros = \strlen($this->value) - \strlen($trimmedValue);
  417. if ($trimmableZeros === 0) {
  418. return $this;
  419. }
  420. if ($trimmableZeros > $this->scale) {
  421. $trimmableZeros = $this->scale;
  422. }
  423. $value = \substr($this->value, 0, -$trimmableZeros);
  424. $scale = $this->scale - $trimmableZeros;
  425. return new BigDecimal($value, $scale);
  426. }
  427. /**
  428. * Returns the absolute value of this number.
  429. */
  430. public function abs() : BigDecimal
  431. {
  432. return $this->isNegative() ? $this->negated() : $this;
  433. }
  434. /**
  435. * Returns the negated value of this number.
  436. */
  437. public function negated() : BigDecimal
  438. {
  439. return new BigDecimal(Calculator::get()->neg($this->value), $this->scale);
  440. }
  441. #[Override]
  442. public function compareTo(BigNumber|int|float|string $that) : int
  443. {
  444. $that = BigNumber::of($that);
  445. if ($that instanceof BigInteger) {
  446. $that = $that->toBigDecimal();
  447. }
  448. if ($that instanceof BigDecimal) {
  449. [$a, $b] = $this->scaleValues($this, $that);
  450. return Calculator::get()->cmp($a, $b);
  451. }
  452. return - $that->compareTo($this);
  453. }
  454. #[Override]
  455. public function getSign() : int
  456. {
  457. return ($this->value === '0') ? 0 : (($this->value[0] === '-') ? -1 : 1);
  458. }
  459. public function getUnscaledValue() : BigInteger
  460. {
  461. return self::newBigInteger($this->value);
  462. }
  463. public function getScale() : int
  464. {
  465. return $this->scale;
  466. }
  467. /**
  468. * Returns the number of significant digits in the number.
  469. *
  470. * This is the number of digits to both sides of the decimal point, stripped of leading zeros.
  471. * The sign has no impact on the result.
  472. *
  473. * Examples:
  474. * 0 => 0
  475. * 0.0 => 0
  476. * 123 => 3
  477. * 123.456 => 6
  478. * 0.00123 => 3
  479. * 0.0012300 => 5
  480. */
  481. public function getPrecision(): int
  482. {
  483. $value = $this->value;
  484. if ($value === '0') {
  485. return 0;
  486. }
  487. $length = \strlen($value);
  488. return ($value[0] === '-') ? $length - 1 : $length;
  489. }
  490. /**
  491. * Returns a string representing the integral part of this decimal number.
  492. *
  493. * Example: `-123.456` => `-123`.
  494. */
  495. public function getIntegralPart() : string
  496. {
  497. if ($this->scale === 0) {
  498. return $this->value;
  499. }
  500. $value = $this->getUnscaledValueWithLeadingZeros();
  501. return \substr($value, 0, -$this->scale);
  502. }
  503. /**
  504. * Returns a string representing the fractional part of this decimal number.
  505. *
  506. * If the scale is zero, an empty string is returned.
  507. *
  508. * Examples: `-123.456` => '456', `123` => ''.
  509. */
  510. public function getFractionalPart() : string
  511. {
  512. if ($this->scale === 0) {
  513. return '';
  514. }
  515. $value = $this->getUnscaledValueWithLeadingZeros();
  516. return \substr($value, -$this->scale);
  517. }
  518. /**
  519. * Returns whether this decimal number has a non-zero fractional part.
  520. */
  521. public function hasNonZeroFractionalPart() : bool
  522. {
  523. return $this->getFractionalPart() !== \str_repeat('0', $this->scale);
  524. }
  525. #[Override]
  526. public function toBigInteger() : BigInteger
  527. {
  528. $zeroScaleDecimal = $this->scale === 0 ? $this : $this->dividedBy(1, 0);
  529. return self::newBigInteger($zeroScaleDecimal->value);
  530. }
  531. #[Override]
  532. public function toBigDecimal() : BigDecimal
  533. {
  534. return $this;
  535. }
  536. #[Override]
  537. public function toBigRational() : BigRational
  538. {
  539. $numerator = self::newBigInteger($this->value);
  540. $denominator = self::newBigInteger('1' . \str_repeat('0', $this->scale));
  541. return self::newBigRational($numerator, $denominator, false);
  542. }
  543. #[Override]
  544. public function toScale(int $scale, RoundingMode $roundingMode = RoundingMode::UNNECESSARY) : BigDecimal
  545. {
  546. if ($scale === $this->scale) {
  547. return $this;
  548. }
  549. return $this->dividedBy(BigDecimal::one(), $scale, $roundingMode);
  550. }
  551. #[Override]
  552. public function toInt() : int
  553. {
  554. return $this->toBigInteger()->toInt();
  555. }
  556. #[Override]
  557. public function toFloat() : float
  558. {
  559. return (float) (string) $this;
  560. }
  561. #[Override]
  562. public function __toString() : string
  563. {
  564. if ($this->scale === 0) {
  565. return $this->value;
  566. }
  567. $value = $this->getUnscaledValueWithLeadingZeros();
  568. return \substr($value, 0, -$this->scale) . '.' . \substr($value, -$this->scale);
  569. }
  570. /**
  571. * This method is required for serializing the object and SHOULD NOT be accessed directly.
  572. *
  573. * @internal
  574. *
  575. * @return array{value: string, scale: int}
  576. */
  577. public function __serialize(): array
  578. {
  579. return ['value' => $this->value, 'scale' => $this->scale];
  580. }
  581. /**
  582. * This method is only here to allow unserializing the object and cannot be accessed directly.
  583. *
  584. * @internal
  585. * @psalm-suppress RedundantPropertyInitializationCheck
  586. *
  587. * @param array{value: string, scale: int} $data
  588. *
  589. * @throws \LogicException
  590. */
  591. public function __unserialize(array $data): void
  592. {
  593. if (isset($this->value)) {
  594. throw new \LogicException('__unserialize() is an internal function, it must not be called directly.');
  595. }
  596. $this->value = $data['value'];
  597. $this->scale = $data['scale'];
  598. }
  599. /**
  600. * Puts the internal values of the given decimal numbers on the same scale.
  601. *
  602. * @return array{string, string} The scaled integer values of $x and $y.
  603. */
  604. private function scaleValues(BigDecimal $x, BigDecimal $y) : array
  605. {
  606. $a = $x->value;
  607. $b = $y->value;
  608. if ($b !== '0' && $x->scale > $y->scale) {
  609. $b .= \str_repeat('0', $x->scale - $y->scale);
  610. } elseif ($a !== '0' && $x->scale < $y->scale) {
  611. $a .= \str_repeat('0', $y->scale - $x->scale);
  612. }
  613. return [$a, $b];
  614. }
  615. private function valueWithMinScale(int $scale) : string
  616. {
  617. $value = $this->value;
  618. if ($this->value !== '0' && $scale > $this->scale) {
  619. $value .= \str_repeat('0', $scale - $this->scale);
  620. }
  621. return $value;
  622. }
  623. /**
  624. * Adds leading zeros if necessary to the unscaled value to represent the full decimal number.
  625. */
  626. private function getUnscaledValueWithLeadingZeros() : string
  627. {
  628. $value = $this->value;
  629. $targetLength = $this->scale + 1;
  630. $negative = ($value[0] === '-');
  631. $length = \strlen($value);
  632. if ($negative) {
  633. $length--;
  634. }
  635. if ($length >= $targetLength) {
  636. return $this->value;
  637. }
  638. if ($negative) {
  639. $value = \substr($value, 1);
  640. }
  641. $value = \str_pad($value, $targetLength, '0', STR_PAD_LEFT);
  642. if ($negative) {
  643. $value = '-' . $value;
  644. }
  645. return $value;
  646. }
  647. }