Skip to content
This repository was archived by the owner on Dec 27, 2023. It is now read-only.

Commit 939fc62

Browse files
committed
Rebase of the branch and resolution of conflict
2 parents 9d321f8 + 495cc75 commit 939fc62

13 files changed

+396
-86
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ php-bignumbers [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://git
22
==============
33

44
A robust library to handle immutable big numbers inside PHP applications
5-
The current stable version is 0.7.0. Litipk\BigNumbers supports PHP 5.3.x, 5.4.x, 5.5.x, and 5.6.,x
5+
The current stable version is 0.7.1. Litipk\BigNumbers supports PHP 5.3.x, 5.4.x, 5.5.x, and 5.6.,x
66
but also Facebook's [HHVM](http://www.hhvm.com).
77

88

@@ -26,7 +26,7 @@ composer.json file the following text:
2626
```json
2727
{
2828
"require": {
29-
"litipk/php-bignumbers": "0.7.0"
29+
"litipk/php-bignumbers": "0.7.1"
3030
}
3131
}
3232
```

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"litipk/php-exceptions": ">=0.1.3"
2121
},
2222
"require-dev": {
23+
"psy/psysh": ">=0.4.4",
2324
"phpunit/phpunit": "4.5.*",
2425
"squizlabs/php_codesniffer": "2.3.*",
2526
"satooshi/php-coveralls": "dev-master"

src/Decimal.php

Lines changed: 93 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Litipk\BigNumbers;
44

5+
use Litipk\BigNumbers\DecimalConstants as DecimalConstants;
56
use Litipk\BigNumbers\InfiniteDecimal as InfiniteDecimal;
67

78
use Litipk\Exceptions\NotImplementedException as NotImplementedException;
@@ -66,17 +67,19 @@ public static function getNegativeInfinite()
6667
/**
6768
* Decimal "constructor".
6869
*
69-
* @param mixed $value
70-
* @param integer $scale
70+
* @param mixed $value
71+
* @param integer $scale
72+
* @param boolean $removeZeros If true then removes trailing zeros from the number representation
73+
* @return Decimal
7174
*/
72-
public static function create($value, $scale = null)
75+
public static function create($value, $scale = null, $removeZeros = false)
7376
{
7477
if (is_int($value)) {
75-
return self::fromInteger($value, $scale);
78+
return self::fromInteger($value);
7679
} elseif (is_float($value)) {
77-
return self::fromFloat($value, $scale);
80+
return self::fromFloat($value, $scale, $removeZeros);
7881
} elseif (is_string($value)) {
79-
return self::fromString($value, $scale);
82+
return self::fromString($value, $scale, $removeZeros);
8083
} elseif ($value instanceof Decimal) {
8184
return self::fromDecimal($value, $scale);
8285
} else {
@@ -90,12 +93,11 @@ public static function create($value, $scale = null)
9093

9194
/**
9295
* @param integer $intValue
93-
* @param integer $scale
9496
* @return Decimal
9597
*/
96-
public static function fromInteger($intValue, $scale = null)
98+
public static function fromInteger($intValue)
9799
{
98-
self::paramsValidation($intValue, $scale);
100+
self::paramsValidation($intValue, null);
99101

100102
if (!is_int($intValue)) {
101103
throw new InvalidArgumentTypeException(
@@ -105,18 +107,16 @@ public static function fromInteger($intValue, $scale = null)
105107
);
106108
}
107109

108-
return new Decimal(
109-
$scale === null ? (string)$intValue : bcadd((string)$intValue, '0', $scale),
110-
$scale === null ? 0 : $scale
111-
);
110+
return new Decimal((string)$intValue, 0);
112111
}
113112

114113
/**
115114
* @param float $fltValue
116115
* @param integer $scale
116+
* @param boolean $removeZeros If true then removes trailing zeros from the number representation
117117
* @return Decimal
118118
*/
119-
public static function fromFloat($fltValue, $scale = null)
119+
public static function fromFloat($fltValue, $scale = null, $removeZeros = false)
120120
{
121121
self::paramsValidation($fltValue, $scale);
122122

@@ -138,22 +138,23 @@ public static function fromFloat($fltValue, $scale = null)
138138
);
139139
}
140140

141-
$dec_scale = $scale === null ?
142-
8 :
143-
$scale;
141+
$scale = ($scale === null) ? 8 : $scale;
144142

145-
return new Decimal(
146-
number_format($fltValue, $dec_scale, '.', ''),
147-
$dec_scale
148-
);
143+
$strValue = number_format($fltValue, $scale, '.', '');
144+
if ($removeZeros) {
145+
$strValue = self::removeTrailingZeros($strValue, $scale);
146+
}
147+
148+
return new Decimal($strValue, $scale);
149149
}
150150

151151
/**
152152
* @param string $strValue
153153
* @param integer $scale
154+
* @param boolean $removeZeros If true then removes trailing zeros from the number representation
154155
* @return Decimal
155156
*/
156-
public static function fromString($strValue, $scale = null)
157+
public static function fromString($strValue, $scale = null, $removeZeros = false)
157158
{
158159
self::paramsValidation($strValue, $scale);
159160

@@ -184,10 +185,10 @@ public static function fromString($strValue, $scale = null)
184185
}
185186

186187
$value = self::normalizeSign($captures[1]) . bcmul(
187-
$captures[2],
188-
$tmp_multiplier,
189-
max($min_scale, $scale !== null ? $scale : 0)
190-
);
188+
$captures[2],
189+
$tmp_multiplier,
190+
max($min_scale, $scale !== null ? $scale : 0)
191+
);
191192

192193
} else if (preg_match('/([+\-]?)(inf|Inf|INF)/', $strValue, $captures) === 1) {
193194
if ($captures[1] === '-') {
@@ -201,13 +202,15 @@ public static function fromString($strValue, $scale = null)
201202
);
202203
}
203204

204-
if ($scale!==null) {
205-
$dec_scale = $scale;
206-
} else {
207-
$dec_scale = $min_scale;
205+
$scale = ($scale!==null) ? $scale : $min_scale;
206+
if ($scale < $min_scale) {
207+
$value = self::innerRound($value, $scale);
208+
}
209+
if ($removeZeros) {
210+
$value = self::removeTrailingZeros($value, $scale);
208211
}
209212

210-
return new Decimal(self::innerRound($value, $dec_scale), $dec_scale);
213+
return new Decimal($value, $scale);
211214
}
212215

213216
/**
@@ -223,7 +226,7 @@ public static function fromDecimal(Decimal $decValue, $scale = null)
223226
self::paramsValidation($decValue, $scale);
224227

225228
// This block protect us from unnecessary additional instances
226-
if ($scale === null || $scale === $decValue->scale || $decValue->isInfinite()) {
229+
if ($scale === null || $scale >= $decValue->scale || $decValue->isInfinite()) {
227230
return $decValue;
228231
}
229232

@@ -286,7 +289,7 @@ public function mul(Decimal $b, $scale = null)
286289
if ($b->isInfinite()) {
287290
return $b->mul($this);
288291
} elseif ($b->isZero()) {
289-
return Decimal::fromInteger(0, $scale);
292+
return DecimalConstants::Zero();
290293
}
291294

292295
return self::fromString(
@@ -311,10 +314,8 @@ public function div(Decimal $b, $scale = null)
311314

312315
if ($b->isZero()) {
313316
throw new \DomainException("Division by zero is not allowed.");
314-
} elseif ($this->isZero()) {
315-
return self::fromDecimal($this, $scale);
316-
} elseif ($b->isInfinite()) {
317-
return Decimal::fromInteger(0, $scale);
317+
} elseif ($this->isZero() || $b->isInfinite()) {
318+
return DecimalConstants::Zero();
318319
} else {
319320
if ($scale !== null) {
320321
$divscale = $scale;
@@ -327,19 +328,18 @@ public function div(Decimal $b, $scale = null)
327328
self::innerLog10($this_abs->value, $this_abs->scale, 1) -
328329
self::innerLog10($b_abs->value, $b_abs->scale, 1);
329330

330-
$divscale = max(
331+
$divscale = (int)max(
331332
$this->scale + $b->scale,
332333
max(
333334
self::countSignificativeDigits($this, $this_abs),
334335
self::countSignificativeDigits($b, $b_abs)
335336
) - max(ceil($log10_result), 0),
336-
ceil(-$log10_result)
337+
ceil(-$log10_result) + 1
337338
);
338339
}
339340

340341
return self::fromString(
341-
bcdiv($this->value, $b->value, $divscale+1),
342-
$divscale
342+
bcdiv($this->value, $b->value, $divscale+1), $divscale
343343
);
344344
}
345345
}
@@ -356,7 +356,7 @@ public function sqrt($scale = null)
356356
"Decimal can't handle square roots of negative numbers (it's only for real numbers)."
357357
);
358358
} elseif ($this->isZero()) {
359-
return Decimal::fromDecimal($this, $scale);
359+
return DecimalConstants::Zero();
360360
}
361361

362362
$sqrt_scale = ($scale !== null ? $scale : $this->scale);
@@ -385,7 +385,11 @@ public function pow(Decimal $b, $scale = null)
385385
);
386386
}
387387
} elseif ($b->isZero()) {
388-
return Decimal::fromInteger(1, $scale);
388+
return DecimalConstants::One();
389+
} else if ($b->isNegative()) {
390+
return DecimalConstants::One()->div(
391+
$this->pow($b->additiveInverse()), $scale
392+
);
389393
} elseif ($b->scale == 0) {
390394
$pow_scale = $scale === null ?
391395
max($this->scale, $b->scale) : max($this->scale, $b->scale, $scale);
@@ -415,6 +419,16 @@ public function pow(Decimal $b, $scale = null)
415419
$pow_scale
416420
);
417421
} else { // elseif ($this->isNegative())
422+
if ($b->isInteger()) {
423+
if (preg_match('/^[+\-]?[0-9]*[02468](\.0+)?$/', $b->value, $captures) === 1) {
424+
// $b is an even number
425+
return $this->additiveInverse()->pow($b, $scale);
426+
} else {
427+
// $b is an odd number
428+
return $this->additiveInverse()->pow($b, $scale)->additiveInverse();
429+
}
430+
}
431+
418432
throw new NotImplementedException(
419433
"Usually negative numbers can't be powered to non integer numbers. " .
420434
"The cases where is possible are not implemented."
@@ -445,6 +459,7 @@ public function log10($scale = null)
445459
}
446460

447461
/**
462+
* @param integer $scale
448463
* @return boolean
449464
*/
450465
public function isZero($scale = null)
@@ -470,6 +485,14 @@ public function isNegative()
470485
return ($this->value[0] === '-');
471486
}
472487

488+
/**
489+
* @return boolean
490+
*/
491+
public function isInteger()
492+
{
493+
return (preg_match('/^[+\-]?[0-9]+(\.0+)?$/', $this->value, $captures) === 1);
494+
}
495+
473496
/**
474497
* @return boolean
475498
*/
@@ -509,6 +532,7 @@ public function equals(Decimal $b, $scale = null)
509532
* $this > $b : returns 1 , $this < $b : returns -1 , $this == $b : returns 0
510533
*
511534
* @param Decimal $b
535+
* @param integer $scale
512536
* @return integer
513537
*/
514538
public function comp(Decimal $b, $scale = null)
@@ -657,20 +681,20 @@ public function sin($scale = null) {
657681
$x = $this->mod(DecimalConstants::PI()->mul(Decimal::fromString("2")));
658682

659683
// PI has only 32 significant numbers
660-
$significantNumbers = is_null($scale) ? 32 : $scale;
684+
$significantNumbers = ($scale === null) ? 32 : $scale;
661685

662686
// Next use Maclaurin's theorem to approximate sin with high enough accuracy
663687
// note that the accuracy is depended on the accuracy of the given PI constant
664-
$faculty = Decimal::fromString("1"); // Calculates the faculty under the sign
665-
$xPowerN = Decimal::fromString("1"); // Calculates x^n
666-
$approx = Decimal::fromString("0"); // keeps track of our approximation for sin(x)
688+
$faculty = DecimalConstants::One(); // Calculates the faculty under the sign
689+
$xPowerN = DecimalConstants::One(); // Calculates x^n
690+
$approx = DecimalConstants::Zero(); // keeps track of our approximation for sin(x)
667691

668692
$change = InfiniteDecimal::getPositiveInfinite();
669693

670694
for ($i = 1; !$change->round($significantNumbers)->isZero(); $i++) {
671695
// update x^n and n! for this walkthrough
672696
$xPowerN = $xPowerN->mul($x);
673-
$faculty = $faculty->mul(Decimal::fromString((string) $i));
697+
$faculty = $faculty->mul(Decimal::fromInteger($i));
674698

675699
// only do calculations if n is uneven
676700
// otherwise result is zero anyways
@@ -702,20 +726,20 @@ public function cos($scale = null) {
702726
$x = $this->mod(DecimalConstants::PI()->mul(Decimal::fromString("2")));
703727

704728
// PI has only 32 significant numbers
705-
$significantNumbers = is_null($scale) ? 32 : $scale;
729+
$significantNumbers = ($scale === null) ? 32 : $scale;
706730

707731
// Next use Maclaurin's theorem to approximate sin with high enough accuracy
708732
// note that the accuracy is depended on the accuracy of the given PI constant
709-
$faculty = Decimal::fromString("1"); // Calculates the faculty under the sign
710-
$xPowerN = Decimal::fromString("1"); // Calculates x^n
711-
$approx = Decimal::fromString("1"); // keeps track of our approximation for sin(x)
733+
$faculty = DecimalConstants::One(); // Calculates the faculty under the sign
734+
$xPowerN = DecimalConstants::One(); // Calculates x^n
735+
$approx = DecimalConstants::One(); // keeps track of our approximation for sin(x)
712736

713737
$change = InfiniteDecimal::getPositiveInfinite();
714738

715739
for ($i = 1; !$change->floor($significantNumbers)->isZero(); $i++) {
716740
// update x^n and n! for this walkthrough
717741
$xPowerN = $xPowerN->mul($x);
718-
$faculty = $faculty->mul(Decimal::fromString((string) $i));
742+
$faculty = $faculty->mul(Decimal::fromInteger($i));
719743

720744
// only do calculations if n is uneven
721745
// otherwise result is zero anyways
@@ -989,19 +1013,31 @@ private static function normalizeSign($sign)
9891013
return $sign;
9901014
}
9911015

1016+
private static function removeTrailingZeros($strValue, &$scale)
1017+
{
1018+
preg_match('/^[+\-]?[0-9]+(\.([0-9]*[1-9])?(0+)?)?$/', $strValue, $captures);
1019+
1020+
if (count($captures) === 4) {
1021+
$toRemove = strlen($captures[3]);
1022+
$scale = strlen($captures[2]);
1023+
$strValue = substr($strValue, 0, strlen($strValue)-$toRemove-($scale===0 ? 1 : 0));
1024+
}
1025+
1026+
return $strValue;
1027+
}
1028+
9921029
/**
993-
* Counts the number of significative digits of $val
1030+
* Counts the number of significative digits of $val.
1031+
* Assumes a consistent internal state (without zeros at the end or the start).
9941032
*
9951033
* @param Decimal $val
9961034
* @param Decimal $abs $val->abs()
9971035
* @return integer
9981036
*/
9991037
private static function countSignificativeDigits(Decimal $val, Decimal $abs)
10001038
{
1001-
$one = Decimal::fromInteger(1);
1002-
10031039
return strlen($val->value) - (
1004-
($abs->comp($one) === -1) ? 2 : max($val->scale, 1)
1040+
($abs->comp(DecimalConstants::One()) === -1) ? 2 : max($val->scale, 1)
10051041
) - ($val->isNegative() ? 1 : 0);
10061042
}
10071043
}

0 commit comments

Comments
 (0)