Test Failed
Push — master ( 4c0154...84398e )
by kacper
03:36 queued 01:22
created

BC::setTrimTrailingZeroes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BCMathExtended;
6
7
use Closure;
8
use InvalidArgumentException;
9
10
class BC
11
{
12
    public const COMPARE_EQUAL = 0;
13
    public const COMPARE_LEFT_GRATER = 1;
14
    public const COMPARE_RIGHT_GRATER = -1;
15
16
    protected const DEFAULT_SCALE = 100;
17
18
    protected const MAX_BASE = 256;
19
20
    protected const BIT_OPERATOR_AND = 'and';
21
    protected const BIT_OPERATOR_OR = 'or';
22
    protected const BIT_OPERATOR_XOR = 'xor';
23
24
    protected static $trimTrailingZeroes = true;
25 56
26
    public static function rand(string $min, string $max): string
27 56
    {
28 56
        $max = static::convertScientificNotationToString($max);
29 45
        $min = static::convertScientificNotationToString($min);
30 9
31
        $difference = static::add(static::sub($max, $min), '1');
32
        $randPercent = static::div((string)mt_rand(), (string)mt_getrandmax(), 8);
33 36
34
        return static::add($min, static::mul($difference, $randPercent, 8), 0);
35
    }
36 11
37
    public static function convertScientificNotationToString(string $number): string
38
    {
39 391
        // check if number is in scientific notation, first use stripos as is faster then preg_match
40
        if (false !== stripos($number, 'E') && preg_match('/(-?(\d+\.)?\d+)E([+-]?)(\d+)/i', $number, $regs)) {
41
            // calculate final scale of number
42 391
            $scale = $regs[4] + static::getDecimalsLengthFromNumber($regs[1]);
43
            $pow = static::pow('10', $regs[4], $scale);
44 60
            if ('-' === $regs[3]) {
45 60
                $number = static::div($regs[1], $pow, $scale);
46 60
            } else {
47 25
                $number = static::mul($pow, $regs[1], $scale);
48
            }
49 35
            // remove unnecessary 0 and dot from 0.000 is a 0
50
            $number = static::trimTrailingZeroes($number);
51
        }
52 60
53
        return static::checkNumber($number);
54
    }
55 391
56
    public static function getDecimalsLengthFromNumber(string $number): int
57
    {
58 107
        $check = explode('.', $number);
59
        if (!empty($check[1])) {
60 107
            return strlen($check[1]);
61 107
        }
62 77
63
        return 0;
64
    }
65 30
66
    public static function pow(string $leftOperand, string $rightOperand, ?int $scale = null): string
67
    {
68 161
        $leftOperand = static::convertScientificNotationToString($leftOperand);
69
        $rightOperand = static::convertScientificNotationToString($rightOperand);
70 161
71 161
        if (static::checkIsFloat($rightOperand)) {
72
            if (null === $scale) {
73 161
                $r = static::powFractional($leftOperand, $rightOperand);
74 8
            } else {
75
                $r = static::powFractional($leftOperand, $rightOperand, $scale);
76
            }
77 8
        } elseif (null === $scale) {
78
            $r = bcpow($leftOperand, $rightOperand);
79 153
        } else {
80 85
            $r = bcpow($leftOperand, $rightOperand, $scale);
81
        }
82 71
83
        return static::trimTrailingZeroes($r);
84
    }
85 161
86
    protected static function checkIsFloat(string $number): bool
87
    {
88 288
        return false !== strpos($number, '.');
89
    }
90 288
91
    protected static function powFractional(string $leftOperand, string $rightOperand, ?int $scale = null): string
92
    {
93 8
        // we need to increased scale to get correct results and avoid rounding error
94
        $currentScale = $scale ?? static::getScale();
95
        $increasedScale = $currentScale * 2;
96 8
97 8
        // add zero to trim scale
98
        return static::checkNumber(
99
            static::add(
100 8
                static::exp(static::mul($rightOperand, static::log($leftOperand), $increasedScale)),
101
                '0',
102
                $currentScale
103 38
            )
104
        );
105 38
    }
106
107
    public static function getScale(): int
108
    {
109 38
        if (PHP_VERSION_ID >= 70300) {
110
            /** @noinspection PhpStrictTypeCheckingInspection */
111 38
            /** @noinspection PhpParamsInspection */
112
            return bcscale();
0 ignored issues
show
Bug Best Practice introduced by
The expression return bcscale() returns the type boolean which is incompatible with the type-hinted return integer.
Loading history...
Bug introduced by
The call to bcscale() has too few arguments starting with scale. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

112
            return /** @scrutinizer ignore-call */ bcscale();

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
113
        }
114 43
115
        $sqrt = static::sqrt('2');
116 43
117
        return strlen(substr($sqrt, strpos($sqrt, '.') + 1));
118 43
    }
119 39
120
    public static function sqrt(string $operand, ?int $scale = null): string
121 5
    {
122
        $operand = static::convertScientificNotationToString($operand);
123
124 43
        if (null === $scale) {
125
            $r = bcsqrt($operand);
126
        } else {
127 308
            $r = bcsqrt($operand, $scale);
128
        }
129 308
130 234
        return static::trimTrailingZeroes($r);
131
    }
132
133 308
    protected static function trimTrailingZeroes(string $number): string
134
    {
135
        if (!self::$trimTrailingZeroes) {
136 391
            return $number;
137
        }
138 391
139 391
        if (false !== strpos($number, '.')) {
140 28
            $number = rtrim($number, '0');
141
        }
142
143 388
        return rtrim($number, '.') ?: '0';
144
    }
145
146 181
    protected static function checkNumber(string $number): string
147
    {
148 181
        $number = str_replace('+', '', filter_var($number, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION));
149 181
        if ('-0' === $number || !is_numeric($number)) {
150
            return '0';
151 181
        }
152 51
153
        return $number;
154 130
    }
155
156
    public static function add(string $leftOperand, string $rightOperand, ?int $scale = null): string
157 181
    {
158
        $leftOperand = static::convertScientificNotationToString($leftOperand);
159
        $rightOperand = static::convertScientificNotationToString($rightOperand);
160 16
161
        if (null === $scale) {
162 16
            $r = bcadd($leftOperand, $rightOperand);
163 16
        } else {
164 16
            $r = bcadd($leftOperand, $rightOperand, $scale);
165 16
        }
166
167
        return static::trimTrailingZeroes($r);
168 16
    }
169
170
    public static function exp(string $arg): string
171 178
    {
172
        $scale = static::DEFAULT_SCALE;
173 178
        $result = '1';
174 178
        for ($i = 299; $i > 0; $i--) {
175
            $result = static::add(static::mul(static::div($result, (string)$i, $scale), $arg, $scale), '1', $scale);
176 178
        }
177 41
178
        return $result;
179 140
    }
180
181
    public static function mul(string $leftOperand, string $rightOperand, ?int $scale = null): string
182 178
    {
183
        $leftOperand = static::convertScientificNotationToString($leftOperand);
184
        $rightOperand = static::convertScientificNotationToString($rightOperand);
185 145
186
        if (null === $scale) {
187 145
            $r = bcmul($leftOperand, $rightOperand);
188 145
        } else {
189
            $r = bcmul($leftOperand, $rightOperand, $scale);
190 145
        }
191 1
192
        return static::trimTrailingZeroes($r);
193
    }
194 144
195 40
    public static function div(string $leftOperand, string $rightOperand, ?int $scale = null): string
196
    {
197 111
        $leftOperand = static::convertScientificNotationToString($leftOperand);
198
        $rightOperand = static::convertScientificNotationToString($rightOperand);
199
200 144
        if ('0' === static::trimTrailingZeroes($rightOperand)) {
201
            throw new InvalidArgumentException('Division by zero');
202
        }
203 13
204
        if (null === $scale) {
205 13
            $r = bcdiv($leftOperand, $rightOperand);
206 13
        } else {
207 1
            $r = bcdiv($leftOperand, $rightOperand, $scale);
208
        }
209 12
210 1
        return static::trimTrailingZeroes($r);
211
    }
212 11
213 11
    public static function log(string $arg): string
214 11
    {
215 11
        $arg = static::convertScientificNotationToString($arg);
216 11
        if ($arg === '0') {
217 11
            return '-INF';
218
        }
219 11
        if (static::COMPARE_RIGHT_GRATER === static::comp($arg, '0')) {
220 11
            return 'NAN';
221 11
        }
222 11
        $scale = static::DEFAULT_SCALE;
223
        $m = (string)log((float)$arg);
224 10
        $x = static::sub(static::div($arg, static::exp($m), $scale), '1', $scale);
225
        $res = '0';
226 11
        $pow = '1';
227 11
        $i = 1;
228
        do {
229 11
            $pow = static::mul($pow, $x, $scale);
230
            $sum = static::div($pow, (string)$i, $scale);
231
            if ($i % 2 === 1) {
232 65
                $res = static::add($res, $sum, $scale);
233
            } else {
234 65
                $res = static::sub($res, $sum, $scale);
235 65
            }
236
            $i++;
237 65
        } while (static::comp($sum, '0', $scale));
238 53
239
        return static::add($res, $m, $scale);
240
    }
241 23
242 23
    public static function comp(string $leftOperand, string $rightOperand, ?int $scale = null): int
243 23
    {
244 23
        $leftOperand = static::convertScientificNotationToString($leftOperand);
245
        $rightOperand = static::convertScientificNotationToString($rightOperand);
246
247
        if (null === $scale) {
248 82
            return bccomp($leftOperand, $rightOperand, max(strlen($leftOperand), strlen($rightOperand)));
249
        }
250 82
251 82
        return bccomp(
252
            $leftOperand,
253 82
            $rightOperand,
254 42
            $scale
255
        );
256 41
    }
257
258
    public static function sub(string $leftOperand, string $rightOperand, ?int $scale = null): string
259 82
    {
260
        $leftOperand = static::convertScientificNotationToString($leftOperand);
261
        $rightOperand = static::convertScientificNotationToString($rightOperand);
262 170
263
        if (null === $scale) {
264 170
            $r = bcsub($leftOperand, $rightOperand);
265
        } else {
266
            $r = bcsub($leftOperand, $rightOperand, $scale);
267 2
        }
268
269 2
        return static::trimTrailingZeroes($r);
270 2
    }
271
272 2
    public static function setTrimTrailingZeroes(bool $flag): void
273 2
    {
274
        self::$trimTrailingZeroes = $flag;
275 2
    }
276
277
    public static function max(...$ags): ?string
278 1
    {
279
        $max = null;
280 1
        foreach (static::parseArgs($ags) as $number) {
281 1
            $number = static::convertScientificNotationToString((string)$number);
282 1
            if (null === $max) {
283 1
                $max = $number;
284 1
            } elseif (static::comp($max, $number) === static::COMPARE_RIGHT_GRATER) {
0 ignored issues
show
Bug introduced by
$max of type void is incompatible with the type string expected by parameter $leftOperand of BCMathExtended\BC::comp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

284
            } elseif (static::comp(/** @scrutinizer ignore-type */ $max, $number) === static::COMPARE_RIGHT_GRATER) {
Loading history...
285 1
                $max = $number;
286 1
            }
287
        }
288
289
        return $max;
290 1
    }
291
292
    protected static function parseArgs(array $args): array
293 2
    {
294
        if (is_array($args[0])) {
295 2
            $args = $args[0];
296 2
        }
297
298
        return $args;
299 2
    }
300
301
    public static function min(...$ags): ?string
302 1
    {
303
        $min = null;
304 1
        foreach (static::parseArgs($ags) as $number) {
305 1
            $number = static::convertScientificNotationToString((string)$number);
306 1
            if (null === $min) {
307 1
                $min = $number;
308 1
            } elseif (static::comp($min, $number) === static::COMPARE_LEFT_GRATER) {
0 ignored issues
show
Bug introduced by
$min of type void is incompatible with the type string expected by parameter $leftOperand of BCMathExtended\BC::comp(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

308
            } elseif (static::comp(/** @scrutinizer ignore-type */ $min, $number) === static::COMPARE_LEFT_GRATER) {
Loading history...
309 1
                $min = $number;
310 1
            }
311
        }
312
313
        return $min;
314 1
    }
315
316
    public static function powMod(
317 25
        string $leftOperand,
318
        string $rightOperand,
319 25
        string $modulus,
320 25
        ?int $scale = null
321
    ): string {
322 25
        $leftOperand = static::convertScientificNotationToString($leftOperand);
323
        $rightOperand = static::convertScientificNotationToString($rightOperand);
324 4
325 4
        // bcpowmod in 5.6 have don't calculate correct results if scale is empty
326 4
        if (null === $scale) {
327
            $r = static::mod(static::pow($leftOperand, $rightOperand), $modulus);
328
        } elseif (static::checkIsFloat($leftOperand) || static::checkIsFloat($rightOperand) || static::checkIsFloat(
329 21
                $modulus
330 21
            )) {
331 25
            // cant use bcpowmod here as it don't support floats
332
            $r = static::mod(static::pow($leftOperand, $rightOperand, $scale), $modulus, $scale);
333
        } else {
334
            $r = bcpowmod($leftOperand, $rightOperand, $modulus, $scale);
335 104
        }
336
337 104
        return static::trimTrailingZeroes($r);
0 ignored issues
show
Bug introduced by
It seems like $r can also be of type null; however, parameter $number of BCMathExtended\BC::trimTrailingZeroes() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

337
        return static::trimTrailingZeroes(/** @scrutinizer ignore-type */ $r);
Loading history...
338 104
    }
339 44
340 44
    public static function mod(string $leftOperand, string $modulus, ?int $scale = null): string
341 9
    {
342
        $leftOperand = static::convertScientificNotationToString($leftOperand);
343 44
344
        // bcmod in 7.2 is not working properly - for example bcmod(9.9999E-10, -0.00056, 9) should return '-0.000559999' but returns 0.0000000
345
346 104
        // bcmod in php 5.6< don't support scale and floats
347
        // let use this $x - floor($x/$y) * $y;
348
        if (null === $scale) {
349 75
            $r = static::sub(
350
                $leftOperand,
351 75
                static::mul(static::floor(static::div($leftOperand, $modulus)), $modulus)
352
            );
353
        } else {
354 24
            $r = static::sub(
355
                $leftOperand,
356 24
                static::mul(static::floor(static::div($leftOperand, $modulus, $scale)), $modulus, $scale),
357 24
                $scale
358
            );
359 24
        }
360
361 4
        return static::trimTrailingZeroes($r);
362 4
    }
363 4
364
    public static function floor(string $number): string
365
    {
366 20
        $number = static::convertScientificNotationToString($number);
367 20
        if (static::checkIsFloat($number) && static::checkIsFloatCleanZeros($number)) {
368 24
            $result = 0;
369
            if (static::isNegative($number)) {
370
                --$result;
371
            }
372 48
            $number = static::add($number, (string)$result, 0);
373
        }
374 48
375 48
        return static::checkNumber($number);
376 29
    }
377 29
378 6
    protected static function checkIsFloatCleanZeros(string &$number): bool
379
    {
380 29
        return false !== strpos($number = static::trimTrailingZeroes($number), '.');
381
    }
382
383 48
    protected static function isNegative(string $number): bool
384
    {
385
        return 0 === strncmp('-', $number, 1);
386 10
    }
387
388
    public static function fact(string $arg): string
389
    {
390
        $arg = static::convertScientificNotationToString($arg);
391
392 10
        if (static::checkIsFloat($arg)) {
393 10
            throw new InvalidArgumentException('Number has to be an integer');
394
        }
395
        if (static::isNegative($arg)) {
396 10
            throw new InvalidArgumentException('Number has to be greater than or equal to 0');
397
        }
398 10
399 10
        $return = '1';
400
        for ($i = 2; $i <= $arg; ++$i) {
401
            $return = static::mul($return, (string)$i);
402 4
        }
403
404 6
        return $return;
405
    }
406
407 10
    public static function hexdec(string $hex): string
408
    {
409
        $remainingDigits = substr($hex, 0, -1);
410 54
        $lastDigitToDecimal = (string)hexdec(substr($hex, -1));
411
412 54
        if ('' === $remainingDigits) {
413
            return $lastDigitToDecimal;
414
        }
415
416
        return static::add(static::mul('16', static::hexdec($remainingDigits)), $lastDigitToDecimal, 0);
417
    }
418 54
419 39
    public static function dechex(string $decimal): string
420 39
    {
421 39
        $quotient = static::div($decimal, '16', 0);
422
        $remainderToHex = dechex((int)static::mod($decimal, '16'));
423
424 15
        if (static::comp($quotient, '0') === static::COMPARE_EQUAL) {
425 15
            return $remainderToHex;
426 15
        }
427 15
428
        return static::dechex($quotient) . $remainderToHex;
429
    }
430
431 54
    public static function bitAnd(
432
        string $leftOperand,
433
        string $rightOperand
434 8
    ): string {
435
        return static::bitOperatorHelper($leftOperand, $rightOperand, static::BIT_OPERATOR_AND);
436 8
    }
437
438 8
    protected static function bitOperatorHelper(string $leftOperand, string $rightOperand, string $operator): string
439 1
    {
440
        $leftOperand = static::convertScientificNotationToString($leftOperand);
441 7
        $rightOperand = static::convertScientificNotationToString($rightOperand);
442 1
443
        if (static::checkIsFloat($leftOperand)) {
444
            throw new InvalidArgumentException('Left operator has to be an integer');
445 6
        }
446 6
        if (static::checkIsFloat($rightOperand)) {
447 5
            throw new InvalidArgumentException('Right operator has to be an integer');
448
        }
449
450 6
        $leftOperandNegative = static::isNegative($leftOperand);
451
        $rightOperandNegative = static::isNegative($rightOperand);
452
453 5
        $leftOperand = static::dec2bin(static::abs($leftOperand));
454
        $rightOperand = static::dec2bin(static::abs($rightOperand));
455 5
456 5
        $maxLength = max(strlen($leftOperand), strlen($rightOperand));
457
458 5
        $leftOperand = static::alignBinLength($leftOperand, $maxLength);
459 5
        $rightOperand = static::alignBinLength($rightOperand, $maxLength);
460
461
        if ($leftOperandNegative) {
462 5
            $leftOperand = static::recalculateNegative($leftOperand);
463
        }
464
        if ($rightOperandNegative) {
465 6
            $rightOperand = static::recalculateNegative($rightOperand);
466
        }
467 6
468 6
        $isNegative = false;
469
        $result = '';
470 6
        if (static::BIT_OPERATOR_AND === $operator) {
471 6
            $result = $leftOperand & $rightOperand;
472
            $isNegative = ($leftOperandNegative and $rightOperandNegative);
473
        } elseif (static::BIT_OPERATOR_OR === $operator) {
474 6
            $result = $leftOperand | $rightOperand;
475
            $isNegative = ($leftOperandNegative or $rightOperandNegative);
476
        } elseif (static::BIT_OPERATOR_XOR === $operator) {
477 12
            $result = $leftOperand ^ $rightOperand;
478
            $isNegative = ($leftOperandNegative xor $rightOperandNegative);
479
        }
480
481 12
        if ($isNegative) {
482
            $result = static::recalculateNegative($result);
483
        }
484 36
485
        $result = static::bin2dec($result);
486 36
487 36
        return $isNegative ? '-' . $result : $result;
488
    }
489 36
490 3
    public static function dec2bin(string $number, int $base = self::MAX_BASE): string
491
    {
492 33
        return static::decBaseHelper(
493 3
            $base,
494
            static function (int $base) use ($number) {
495
                $value = '';
496 30
                if ('0' === $number) {
497 30
                    return chr((int)$number);
498
                }
499 30
500 30
                while (BC::comp($number, '0') !== BC::COMPARE_EQUAL) {
501
                    $rest = BC::mod($number, (string)$base);
502 30
                    $number = BC::div($number, (string)$base);
503
                    $value = chr((int)$rest) . $value;
504 30
                }
505 30
506
                return $value;
507 30
            }
508 7
        );
509
    }
510 30
511 7
    protected static function decBaseHelper(int $base, Closure $closure): string
512
    {
513
        if ($base < 2 || $base > static::MAX_BASE) {
514 30
            throw new InvalidArgumentException('Invalid Base: ' . $base);
515 30
        }
516 30
        $orgScale = static::getScale();
517 10
        static::setScale(0);
518 10
519 20
        $value = $closure($base);
520 12
521 12
        static::setScale($orgScale);
522 8
523 8
        return $value;
524 8
    }
525
526
    public static function setScale(int $scale): void
527 30
    {
528 8
        bcscale($scale);
529
    }
530
531 30
    public static function abs(string $number): string
532
    {
533 30
        $number = static::convertScientificNotationToString($number);
534
535
        if (static::isNegative($number)) {
536 36
            $number = (string)substr($number, 1);
537
        }
538 36
539
        return static::checkNumber($number);
540 35
    }
541 35
542 30
    protected static function alignBinLength(string $string, int $length): string
543
    {
544
        return str_pad($string, $length, static::dec2bin('0'), STR_PAD_LEFT);
545 33
    }
546 33
547 33
    protected static function recalculateNegative(string $number): string
548 33
    {
549
        $xor = str_repeat(static::dec2bin((string)(static::MAX_BASE - 1)), strlen($number));
550
        $number ^= $xor;
551 33
        for ($i = strlen($number) - 1; $i >= 0; $i--) {
552 36
            $byte = ord($number[$i]);
553
            if (++$byte !== static::MAX_BASE) {
554
                $number[$i] = chr($byte);
555
                break;
556 37
            }
557
        }
558 37
559 2
        return $number;
560
    }
561 35
562 35
    public static function bin2dec(string $binary, int $base = self::MAX_BASE): string
563
    {
564 35
        return static::decBaseHelper(
565
            $base,
566 35
            static function (int $base) use ($binary) {
567
                $size = strlen($binary);
568 35
                $return = '0';
569
                for ($i = 0; $i < $size; ++$i) {
570
                    $element = ord($binary[$i]);
571 393
                    $power = BC::pow((string)$base, (string)($size - $i - 1));
572
                    $return = BC::add($return, BC::mul((string)$element, $power));
573 393
                }
574 393
575
                return $return;
576 45
            }
577
        );
578 45
    }
579
580 45
    public static function bitOr(string $leftOperand, string $rightOperand): string
581 19
    {
582
        return static::bitOperatorHelper($leftOperand, $rightOperand, static::BIT_OPERATOR_OR);
583
    }
584 45
585
    public static function bitXor(string $leftOperand, string $rightOperand): string
586
    {
587 30
        return static::bitOperatorHelper($leftOperand, $rightOperand, static::BIT_OPERATOR_XOR);
588
    }
589 30
590
    public static function roundHalfEven(string $number, int $precision = 0): string
591
    {
592 11
        $number = static::convertScientificNotationToString($number);
593
        if (!static::checkIsFloat($number)) {
594 11
            return static::checkNumber($number);
595 11
        }
596 11
597 11
        $precessionPos = strpos($number, '.') + $precision + 1;
598 11
        if (strlen($number) <= $precessionPos) {
599 11
            return static::round($number, $precision);
600 11
        }
601
602
        if ($number[$precessionPos] !== '5') {
603
            return static::round($number, $precision);
604 11
        }
605
606
        $isPrevEven = $number[$precessionPos - 1] === '.'
607 36
            ? (int)$number[$precessionPos - 2] % 2 === 0
608
            : (int)$number[$precessionPos - 1] % 2 === 0;
609 36
        $isNegative = static::isNegative($number);
610
611 35
        if ($isPrevEven === $isNegative) {
612 35
            return static::roundUp($number, $precision);
613 35
        }
614 35
615 35
        return static::roundDown($number, $precision);
616 35
    }
617
618
    public static function round(string $number, int $precision = 0): string
619 35
    {
620 36
        $number = static::convertScientificNotationToString($number);
621
        if (static::checkIsFloat($number)) {
622
            if (static::isNegative($number)) {
623
                return static::sub($number, '0.' . str_repeat('0', $precision) . '5', $precision);
624 14
            }
625
626 14
            return static::add($number, '0.' . str_repeat('0', $precision) . '5', $precision);
627
        }
628
629 10
        return static::checkNumber($number);
630
    }
631 10
632
    public static function roundUp(string $number, int $precision = 0): string
633
    {
634 24
        $number = static::convertScientificNotationToString($number);
635
        $multiply = static::pow('10', (string)abs($precision));
636 24
637 24
        return $precision < 0
638
            ?
639
            static::mul(
640
                static::ceil(static::div($number, $multiply, static::getDecimalsLengthFromNumber($number))),
641 24
                $multiply,
642 24
                $precision
643 1
            )
644
            :
645
            static::div(
646 23
                static::ceil(static::mul($number, $multiply, static::getDecimalsLengthFromNumber($number))),
647 8
                $multiply,
648
                $precision
649
            );
650 15
    }
651 4
652 15
    public static function ceil(string $number): string
653
    {
654 15
        $number = static::convertScientificNotationToString($number);
655
        if (static::checkIsFloat($number) && static::checkIsFloatCleanZeros($number)) {
656 15
            $result = 1;
657 7
            if (static::isNegative($number)) {
658
                --$result;
659
            }
660 8
            $number = static::add($number, (string)$result, 0);
661
        }
662
663
        return static::checkNumber($number);
664
    }
665
666
    public static function roundDown(string $number, int $precision = 0): string
667
    {
668
        $number = static::convertScientificNotationToString($number);
669
        $multiply = static::pow('10', (string)abs($precision));
670
671
        return $precision < 0
672
            ?
673
            static::mul(
674
                static::floor(static::div($number, $multiply, static::getDecimalsLengthFromNumber($number))),
675
                $multiply,
676
                $precision
677
            )
678
            :
679
            static::div(
680
                static::floor(static::mul($number, $multiply, static::getDecimalsLengthFromNumber($number))),
681
                $multiply,
682
                $precision
683
            );
684
    }
685
}
686