isPositive() || ! $discountAmount->isPositive()) { return self::zero(); } $basisPoints = intdiv( ((int) $discountAmount->getAmount() * self::MAX_BASIS_POINTS * 2) + (int) $basePrice->getAmount(), (int) $basePrice->getAmount() * 2 ); return new self(min($basisPoints, self::MAX_BASIS_POINTS)); } public function basisPoints(): int { return $this->basisPoints; } /** Ratio between 0.0 and 1.0 */ public function ratio(): float { return $this->basisPoints / self::MAX_BASIS_POINTS; } /** Percentage between 0.0 and 100.0 */ public function percentage(): float { return $this->basisPoints / 100; } public function isZero(): bool { return $this->basisPoints === 0; } public function equals(self $other): bool { return $this->basisPoints === $other->basisPoints; } /** * Returns the discount amount produced by this rate on the given amount. * * @param Money::ROUND_* $roundingMode */ public function applyTo(Money $amount, int $roundingMode = Money::ROUND_UP): Money { return $amount->multiply((string) $this->basisPoints)->divide(self::MAX_BASIS_POINTS, $roundingMode); } #[\Override] public function __toString(): string { return rtrim(rtrim(number_format($this->percentage(), 2, '.', ''), '0'), '.') . ' %'; } }