24 $this->scale = $scale;
32 return extension_loaded(
'gmp');
43 if ($aNum->isDecimal() || $bNum->isDecimal()) {
44 $integersCompared = gmp_cmp($aNum->getIntegerPart(), $bNum->getIntegerPart());
45 if ($integersCompared !== 0) {
46 return $integersCompared;
49 $aNumFractional = $aNum->getFractionalPart() ===
'' ?
'0' : $aNum->getFractionalPart();
50 $bNumFractional = $bNum->getFractionalPart() ===
'' ?
'0' : $bNum->getFractionalPart();
52 return gmp_cmp($aNumFractional, $bNumFractional);
55 return gmp_cmp($a, $b);
61 public function add($amount, $addend)
63 return gmp_strval(gmp_add($amount, $addend));
69 public function subtract($amount, $subtrahend)
71 return gmp_strval(gmp_sub($amount, $subtrahend));
77 public function multiply($amount, $multiplier)
81 if ($multiplier->isDecimal()) {
82 $decimalPlaces = strlen($multiplier->getFractionalPart());
83 $multiplierBase = $multiplier->getIntegerPart();
85 if ($multiplierBase) {
86 $multiplierBase .= $multiplier->getFractionalPart();
88 $multiplierBase = ltrim($multiplier->getFractionalPart(),
'0');
91 $resultBase = gmp_strval(gmp_mul(gmp_init($amount), gmp_init($multiplierBase)));
93 if (
'0' === $resultBase) {
97 $result = substr($resultBase, $decimalPlaces * -1);
98 $resultLength = strlen($result);
99 if ($decimalPlaces > $resultLength) {
100 return '0.'.str_pad(
'', $decimalPlaces - $resultLength,
'0').$result;
103 return substr($resultBase, 0, $decimalPlaces * -1).
'.'.$result;
106 return gmp_strval(gmp_mul(gmp_init($amount), gmp_init((
string) $multiplier)));
112 public function divide($amount, $divisor)
116 if ($divisor->isDecimal()) {
117 $decimalPlaces = strlen($divisor->getFractionalPart());
119 if ($divisor->getIntegerPart()) {
120 $divisor =
new Number($divisor->getIntegerPart().$divisor->getFractionalPart());
122 $divisor =
new Number(ltrim($divisor->getFractionalPart(),
'0'));
125 $amount = gmp_strval(gmp_mul(gmp_init($amount), gmp_init(
'1'.str_pad(
'', $decimalPlaces,
'0'))));
128 list($integer, $remainder) = gmp_div_qr(gmp_init($amount), gmp_init((
string) $divisor));
130 if (gmp_cmp($remainder,
'0') === 0) {
131 return gmp_strval($integer);
134 $divisionOfRemainder = gmp_strval(
136 gmp_mul($remainder, gmp_init(
'1'.str_pad(
'', $this->scale,
'0'))),
137 gmp_init((
string) $divisor),
142 if ($divisionOfRemainder[0] ===
'-') {
143 $divisionOfRemainder = substr($divisionOfRemainder, 1);
146 return gmp_strval($integer).
'.'.str_pad($divisionOfRemainder, $this->scale,
'0', STR_PAD_LEFT);
152 public function ceil($number)
156 if ($number->isInteger()) {
157 return (
string) $number;
160 if ($number->isNegative()) {
161 return $this->
add($number->getIntegerPart(),
'0');
164 return $this->
add($number->getIntegerPart(),
'1');
170 public function floor($number)
174 if ($number->isInteger()) {
175 return (
string) $number;
178 if ($number->isNegative()) {
179 return $this->
add($number->getIntegerPart(),
'-1');
182 return $this->
add($number->getIntegerPart(),
'0');
190 return ltrim($number,
'-');
196 public function round($number, $roundingMode)
200 if ($number->isInteger()) {
201 return (
string) $number;
204 if ($number->isHalf() ===
false) {
205 return $this->roundDigit($number);
210 $number->getIntegerPart(),
211 $number->getIntegerRoundingMultiplier()
216 return $this->
add($number->getIntegerPart(),
'0');
220 if ($number->isCurrentEven()) {
221 return $this->
add($number->getIntegerPart(),
'0');
225 $number->getIntegerPart(),
226 $number->getIntegerRoundingMultiplier()
231 if ($number->isCurrentEven()) {
233 $number->getIntegerPart(),
234 $number->getIntegerRoundingMultiplier()
238 return $this->
add($number->getIntegerPart(),
'0');
242 if ($number->isNegative()) {
244 $number->getIntegerPart(),
250 $number->getIntegerPart(),
251 $number->getIntegerRoundingMultiplier()
256 if ($number->isNegative()) {
258 $number->getIntegerPart(),
259 $number->getIntegerRoundingMultiplier()
264 $number->getIntegerPart(),
269 throw new \InvalidArgumentException(
'Unknown rounding mode');
277 private function roundDigit(Number $number)
279 if ($number->isCloserToNext()) {
281 $number->getIntegerPart(),
282 $number->getIntegerRoundingMultiplier()
286 return $this->
add($number->getIntegerPart(),
'0');
292 public function share($amount, $ratio, $total)
300 public function mod($amount, $divisor)
308 if ($amount->isNegative()) {
309 $remainder = gmp_neg($remainder);
312 return gmp_strval($remainder);
320 $this->assertSame(
'-4.54545454545455', $this->getCalculator()->
divide(
'-500', 110));