• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

brick / money / 22235738976

20 Feb 2026 06:15PM UTC coverage: 97.88% (-0.2%) from 98.05%
22235738976

push

github

BenMorel
Use arbitrary-precision arithmetic for powers of 10

7 of 8 new or added lines in 2 files covered. (87.5%)

554 of 566 relevant lines covered (97.88%)

28.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.12
/src/Context/CustomContext.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Brick\Money\Context;
6

7
use Brick\Math\BigDecimal;
8
use Brick\Math\BigInteger;
9
use Brick\Math\BigNumber;
10
use Brick\Math\RoundingMode;
11
use Brick\Money\Context;
12
use Brick\Money\Currency;
13
use Brick\Money\Exception\MoneyException;
14
use Override;
15

16
use function sprintf;
17

18
/**
19
 * Adjusts a number to a custom scale and optionally step.
20
 */
21
final readonly class CustomContext implements Context
22
{
23
    /**
24
     * @param non-negative-int $scale The scale of the monies using this context.
25
     * @param int<1, max>      $step  An optional cash rounding step. Must either divide 10^scale or be a multiple of 10^scale.
26
     *                                For example, scale=2 and step=5 allows 0.00, 0.05, 0.10, etc.
27
     *                                And scale=2 and step=1000 allows 0.00, 10.00, 20.00, etc.
28
     */
29
    public function __construct(
30
        private int $scale,
31
        private int $step = 1,
32
    ) {
33
        /** @psalm-suppress DocblockTypeContradiction, NoValue */
34
        if ($scale < 0) {
142✔
NEW
35
            throw new MoneyException(sprintf('Invalid scale: %d.', $scale));
×
36
        }
37

38
        if ($step < 1 || ! $this->isValidStepForScale($scale, $step)) {
142✔
39
            throw new MoneyException(sprintf('Invalid step: %d.', $step));
49✔
40
        }
41
    }
42

43
    #[Override]
44
    public function applyTo(BigNumber $amount, Currency $currency, RoundingMode $roundingMode): BigDecimal
45
    {
46
        if ($this->step === 1) {
82✔
47
            return $amount->toScale($this->scale, $roundingMode);
49✔
48
        }
49

50
        return $amount
33✔
51
            ->toBigRational()
33✔
52
            ->dividedBy($this->step)
33✔
53
            ->toScale($this->scale, $roundingMode)
33✔
54
            ->multipliedBy($this->step);
33✔
55
    }
56

57
    #[Override]
58
    public function getStep(): int
59
    {
60
        return $this->step;
53✔
61
    }
62

63
    #[Override]
64
    public function isFixedScale(): bool
65
    {
66
        return true;
1✔
67
    }
68

69
    /**
70
     * Returns the scale used by this context.
71
     */
72
    public function getScale(): int
73
    {
74
        return $this->scale;
1✔
75
    }
76

77
    /**
78
     * @param non-negative-int $scale
79
     * @param int<1, max>      $step
80
     */
81
    private function isValidStepForScale(int $scale, int $step): bool
82
    {
83
        $step = BigInteger::of($step);
122✔
84
        $power = BigInteger::ten()->power($scale);
122✔
85

86
        return $power->mod($step)->isZero() || $step->mod($power)->isZero();
122✔
87
    }
88
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc