Skip to content

Commit 42e9bc8

Browse files
Merge pull request #3 from PrestaShop/develop
Fixed trailing zeroes ambiguity
2 parents b39e1c3 + d4dc396 commit 42e9bc8

File tree

3 files changed

+246
-27
lines changed

3 files changed

+246
-27
lines changed

README.md

Lines changed: 105 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,22 @@
77

88
An object-oriented [BC Math extension](http://php.net/manual/en/book.bc.php) wrapper/shim.
99

10-
**Decimal** offers an object-oriented implementation of basic math operation with arbitrary precision, using BC Math if available.
10+
**Decimal** offers a stateless, fluent object-oriented implementation of basic arbitrary-precision arithmetic, using BC Math if available.
11+
12+
You can find out more about floating point precision [here](http://php.net/float).
13+
14+
Example:
15+
```php
16+
use PrestaShop\Decimal\Number;
17+
use PrestaShop\Decimal\Operation\Rounding;
18+
19+
echo (new Number('0.1'))
20+
->plus(new Number('0.7'))
21+
->times(new Number('10'))
22+
->round(0, Rounding::ROUND_FLOOR)
23+
24+
// echoes '8'
25+
```
1126

1227
## Install
1328

@@ -17,21 +32,43 @@ Via Composer
1732
$ composer require prestashop/decimal
1833
```
1934

20-
## Usage
35+
## Usage reference
2136

22-
### Instantiation
37+
Quick links:
38+
- [Instantiation](#instantiation)
39+
- [Addition](#addition)
40+
- [Subtraction](#subtraction)
41+
- [Multiplication](#multiplication)
42+
- [Division](#division)
43+
- [Comparison](#comparison)
44+
- [Fixed precision](#fixed-precision)
45+
- [Rounding](#rounding)
46+
- [Dot shifting](#dot-shifting)
47+
- [Useful methods](#useful-methods)
2348

49+
### Instantiation
50+
Creates a new Decimal number.
51+
```php
52+
public Number Number::__construct ( string $number [, int $exponent = null ] )
53+
```
54+
There are two ways to instantiate a Decimal\Number:
2455
``` php
2556
// create a number from string
2657
$number = new PrestaShop\Decimal\Number('123.456');
2758
echo $number; // echoes '123.456'
28-
59+
```
60+
``` php
2961
// exponent notation
3062
$number = new PrestaShop\Decimal\Number('123456', -3);
3163
echo $number; // echoes '123.456'
3264
```
3365

3466
### Addition
67+
Returns the computed result of adding another number to the current one.
68+
```php
69+
public Number Number::plus ( Number $addend )
70+
```
71+
Examples:
3572
```php
3673
$a = new PrestaShop\Decimal\Number('123.456');
3774
$b = new PrestaShop\Decimal\Number('654.321');
@@ -40,6 +77,11 @@ echo $a->plus($b); // echoes '777.777'
4077
```
4178

4279
### Subtraction
80+
Returns the computed result of subtracting another number to the current one.
81+
```php
82+
public Number Number::minus ( Number $subtrahend )
83+
```
84+
Examples:
4385
```php
4486
$a = new PrestaShop\Decimal\Number('777.777');
4587
$b = new PrestaShop\Decimal\Number('654.321');
@@ -48,6 +90,11 @@ echo $a->minus($b); // echoes '123.456'
4890
```
4991

5092
### Multiplication
93+
Returns the computed result of multiplying the current number with another one.
94+
```php
95+
public Number Number::times ( Number $factor )
96+
```
97+
Examples:
5198
```php
5299
$a = new PrestaShop\Decimal\Number('777.777');
53100
$b = new PrestaShop\Decimal\Number('654.321');
@@ -56,6 +103,11 @@ echo $a->times($b); // echoes '508915.824417'
56103
```
57104

58105
### Division
106+
Returns the computed result of dividing the current number by another one, with up to a certain number of decimal positions (6 by default).
107+
```php
108+
public Number Number::dividedBy ( Number $divisor [, int $precision = Operation\Division::DEFAULT_PRECISION ] )
109+
```
110+
Examples:
59111
```php
60112
$a = new PrestaShop\Decimal\Number('777.777');
61113
$b = new PrestaShop\Decimal\Number('654.321');
@@ -67,15 +119,24 @@ echo $a->dividedBy($b, 15); // echoes '1.188678034175886'
67119
```
68120

69121
### Comparison
122+
Returns the result of the comparison assertion.
70123
```php
71-
$a->equals($b);
72-
$a->isLowerThan($b);
73-
$a->isLowerOrEqualThan($b);
74-
$a->isGreaterThan($b);
75-
$a->isGreaterOrEqualThan($b);
124+
$a = new PrestaShop\Decimal\Number('777.777');
125+
$b = new PrestaShop\Decimal\Number('654.321');
126+
127+
$a->equals($b); // returns false
128+
$a->isLowerThan($b); // returns false
129+
$a->isLowerOrEqualThan($b); // returns false
130+
$a->isGreaterThan($b); // returns true
131+
$a->isGreaterOrEqualThan($b); // returns true
76132
```
77133

78-
### Rounding
134+
### Fixed precision
135+
Returns the number as a string, optionally rounded, with an exact number of decimal positions.
136+
```php
137+
public string Number::toPrecision ( int $precision [, string $roundingMode = Rounding::ROUND_TRUNCATE ] )
138+
```
139+
Examples:
79140
```php
80141
$a = new PrestaShop\Decimal\Number('123.456');
81142
$a = new PrestaShop\Decimal\Number('-123.456');
@@ -146,7 +207,36 @@ $a->toPrecision(9, PrestaShop\Decimal\Operation\Rounding::ROUND_HALF_EVEN); //
146207
$a->toPrecision(10, PrestaShop\Decimal\Operation\Rounding::ROUND_HALF_EVEN); // '1.1525354556'
147208
```
148209

210+
### Rounding
211+
Rounding behaves like `toPrecision`, but provides "up to" a certain number of decimal positions
212+
(it does not add trailing zeroes).
213+
```php
214+
public string Number::round ( int $maxDecimals [, string $roundingMode = Rounding::ROUND_TRUNCATE ] )
215+
```
216+
Examples:
217+
```php
218+
$a = new PrestaShop\Decimal\Number('123.456');
219+
$a = new PrestaShop\Decimal\Number('-123.456');
220+
221+
// truncate / pad
222+
$a->round(0); // '123'
223+
$a->round(1); // '123.4'
224+
$a->round(2); // '123.45'
225+
$a->round(3); // '123.456'
226+
$a->round(4); // '123.456'
227+
$b->round(0); // '-123'
228+
$b->round(1); // '-123.4'
229+
$b->round(2); // '-123.45'
230+
$b->round(3); // '-123.456'
231+
$b->round(4); // '-123.456'
232+
```
233+
149234
### Dot shifting
235+
Creates a new copy of this number multiplied by 10^exponent
236+
```php
237+
public Number Number::toMagnitude ( int $exponent )
238+
```
239+
Examples:
150240
```php
151241
$a = new Decimal\Number('123.456789');
152242

@@ -159,16 +249,16 @@ $a->toMagnitude(3); // 123456.789
159249

160250
### Useful methods
161251
```php
162-
$number = new PrestaShop\Decimal\Number('123.456');
252+
$number = new PrestaShop\Decimal\Number('123.45');
163253
$number->getIntegerPart(); // '123'
164-
$number->getFractionalPart(); // '456'
165-
$number->getPrecision(); // '3' (number of decimals)
254+
$number->getFractionalPart(); // '45'
255+
$number->getPrecision(); // '2' (number of decimals)
166256
$number->getSign(); // '' ('-' if the number was negative)
167-
$number->getExponent(); // '3' (always positive)
257+
$number->getExponent(); // '2' (always positive)
168258
$number->getCoefficient(); // '123456'
169259
$number->isPositive(); // true
170260
$number->isNegative(); // false
171-
$number->invert(); // new Decimal\Number('-123.456')
261+
$number->invert(); // new Decimal\Number('-123.45')
172262
```
173263

174264
## Testing

src/Number.php

Lines changed: 61 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -191,24 +191,76 @@ public function __toString()
191191
}
192192

193193
/**
194-
* Returns the number as a string, rounded to a specified precision
195-
* @param $precision
196-
* @param string $roundingMode
194+
* Returns the number as a string, with exactly $precision decimals
195+
*
196+
* Example:
197+
* ```
198+
* $n = new Number('123.4560');
199+
* (string) $n->round(1); // '123.4'
200+
* (string) $n->round(2); // '123.45'
201+
* (string) $n->round(3); // '123.456'
202+
* (string) $n->round(4); // '123.4560' (trailing zeroes are added)
203+
* (string) $n->round(5); // '123.45600' (trailing zeroes are added)
204+
* ```
205+
*
206+
* @param int $precision Exact number of desired decimals
207+
* @param string $roundingMode [default=Rounding::ROUND_TRUNCATE] Rounding algorithm
197208
*
198209
* @return string
199210
*/
200211
public function toPrecision($precision, $roundingMode = Rounding::ROUND_TRUNCATE)
201212
{
202-
if ($precision > $this->getPrecision()) {
213+
$currentPrecision = $this->getPrecision();
214+
215+
if ($precision === $currentPrecision) {
216+
return (string) $this;
217+
}
218+
219+
$return = $this;
220+
221+
if ($precision < $currentPrecision) {
222+
$return = (new Operation\Rounding())->compute($this, $precision, $roundingMode);
223+
}
224+
225+
if ($precision > $return->getPrecision()) {
203226
return (
204-
$this->getSign()
205-
. $this->getIntegerPart()
206-
. '.'
207-
. str_pad($this->getFractionalPart(), $precision, '0')
227+
$return->getSign()
228+
.$return->getIntegerPart()
229+
.'.'
230+
.str_pad($return->getFractionalPart(), $precision, '0')
208231
);
209232
}
210233

211-
return (new Operation\Rounding())->compute($this, $precision, $roundingMode);
234+
return (string) $return;
235+
}
236+
237+
/**
238+
* Returns the number as a string, with up to $maxDecimals significant digits.
239+
*
240+
* Example:
241+
* ```
242+
* $n = new Number('123.4560');
243+
* (string) $n->round(1); // '123.4'
244+
* (string) $n->round(2); // '123.45'
245+
* (string) $n->round(3); // '123.456'
246+
* (string) $n->round(4); // '123.456' (does not add trailing zeroes)
247+
* (string) $n->round(5); // '123.456' (does not add trailing zeroes)
248+
* ```
249+
*
250+
* @param int $maxDecimals Maximum number of decimals
251+
* @param string $roundingMode [default=Rounding::ROUND_TRUNCATE] Rounding algorithm
252+
*
253+
* @return string
254+
*/
255+
public function round($maxDecimals, $roundingMode = Rounding::ROUND_TRUNCATE)
256+
{
257+
$currentPrecision = $this->getPrecision();
258+
259+
if ($maxDecimals < $currentPrecision) {
260+
return (string) (new Operation\Rounding())->compute($this, $maxDecimals, $roundingMode);
261+
}
262+
263+
return (string) $this;
212264
}
213265

214266
/**

0 commit comments

Comments
 (0)