Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

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