8000 bug #9895 [Intl] Added round support for ROUND_CEILING, ROUND_FLOOR, … · symfony/symfony@bee8a99 · GitHub
[go: up one dir, main page]

Skip to content

Commit bee8a99

Browse files
committed
bug #9895 [Intl] Added round support for ROUND_CEILING, ROUND_FLOOR, ROUND_DOWN, ROUND_UP (pamil)
This PR was squashed before being merged into the 2.4 branch (closes #9895). Discussion ---------- [Intl] Added round support for ROUND_CEILING, ROUND_FLOOR, ROUND_DOWN, ROUND_UP | Q | A | ------------- | --- | Bug fix? | yes | New feature? | yes | BC breaks? | no, actually it fixes some | Deprecations? | no | Tests pass? | yes | Fixed tickets | #9838 | License | MIT | Doc PR | Adds support for 4 unsupported rounding modes (ROUND_CEILING, ROUND_FLOOR, ROUND_DOWN, ROUND_UP) in `NumberFormatter` from `Intl` component, so that `Symfony\Component\Form\Extension\Core\DataTransformer\IntegerToLocalizedStringTransformer` won't throw exception if `lib-intl` is not installed. Commits ------- f5fee9a [Intl] Added round support for ROUND_CEILING, ROUND_FLOOR, ROUND_DOWN, ROUND_UP
2 parents 37813bd + f5fee9a commit bee8a99

File tree

2 files changed

+152
-18
lines changed

2 files changed

+152
-18
lines changed

src/Symfony/Component/Intl/NumberFormatter/NumberFormatter.php

Lines changed: 56 additions & 18 deletions
< 10000 /tr>
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class NumberFormatter
154154
private $attributes = array(
155155
self::FRACTION_DIGITS => 0,
156156
self::GROUPING_USED => 1,
157-
self::ROUNDING_MODE => self::ROUND_HALFEVEN
157+
self::ROUNDING_MODE => self::ROUND_HALFEVEN,
158158
);
159159

160160
/**
@@ -171,7 +171,7 @@ class NumberFormatter
171171
*/
172172
private static $supportedStyles = array(
173173
'CURRENCY' => self::CURRENCY,
174-
'DECIMAL' => self::DECIMAL
174+
'DECIMAL' => self::DECIMAL,
175175
);
176176

177177
/**
@@ -182,7 +182,7 @@ class NumberFormatter
182182
private static $supportedAttributes = array(
183183
'FRACTION_DIGITS' => self::FRACTION_DIGITS,
184184
'GROUPING_USED' => self::GROUPIN 8000 G_USED,
185-
'ROUNDING_MODE' => self::ROUNDING_MODE
185+
'ROUNDING_MODE' => self::ROUNDING_MODE,
186186
);
187187

188188
/**
@@ -195,7 +195,11 @@ class NumberFormatter
195195
private static $roundingModes = array(
196196
'ROUND_HALFEVEN' => self::ROUND_HALFEVEN,
197197
'ROUND_HALFDOWN' => self::ROUND_HALFDOWN,
198-
'ROUND_HALFUP' => self::ROUND_HALFUP
198+
'ROUND_HALFUP' => self::ROUND_HALFUP,
199+
'ROUND_CEILING' => self::ROUND_CEILING,
200+
'ROUND_FLOOR' => self::ROUND_FLOOR,
201+
'ROUND_DOWN' => self::ROUND_DOWN,
202+
'ROUND_UP' => self::ROUND_UP,
199203
);
200204

201205
/**
@@ -209,7 +213,21 @@ class NumberFormatter
209213
private static $phpRoundingMap = array(
210214
self::ROUND_HALFDOWN => \PHP_ROUND_HALF_DOWN,
211215
self::ROUND_HALFEVEN => \PHP_ROUND_HALF_EVEN,
212-
self::ROUND_HALFUP => \PHP_ROUND_HALF_UP
216+
self::ROUND_HALFUP => \PHP_ROUND_HALF_UP,
217+
);
218+
219+
/**
220+
* The list of supported rounding modes which aren't available modes in
221+
* PHP's round() function, but there's an equivalent. Keys are rounding
222+
* modes, values does not matter.
223+
*
224+
* @var array
225+
*/
226+
private static $customRoundingList = array(
227+
self::ROUND_CEILING => true,
228+
self::ROUND_FLOOR => true,
229+
self::ROUND_DOWN => true,
230+
self::ROUND_UP => true,
213231
);
214232

215233
/**
@@ -219,7 +237,7 @@ class NumberFormatter
219237
*/
220238
private static $int32Range = array(
221239
'positive' => 2147483647,
222-
'negative' => -2147483648
240+
'negative' => -2147483648,
223241
);
224242

225243
/**
@@ -229,7 +247,7 @@ class NumberFormatter
229247
*/
230248
private static $int64Range = array(
231249
'positive' => 9223372036854775807,
232-
'negative' => -9223372036854775808
250+
'negative' => -9223372036854775808,
233251
);
234252

235253
private static $enSymbols = array(
@@ -505,7 +523,7 @@ public function parseCurrency($value, &$currency, &$position = null)
505523
* Parse a number
506524
*
507525
* @param string $value The value to parse
508-
* @param string $type Type of the formatting, one of the format type constants. NumberFormatter::TYPE_DOUBLE by default
526+
* @param int $type Type of the formatting, one of the format type constants. NumberFormatter::TYPE_DOUBLE by default
509527
* @param int $position Offset to begin the parsing on return this value will hold the offset at which the parsing ended
510528
*
511529
* @return Boolean|string The parsed value of false on error
@@ -549,9 +567,7 @@ public function parse($value, $type = self::TYPE_DOUBLE, &$position = 0)
549567
* @param int $attr An attribute specifier, one of the numeric attribute constants.
550568
* The only currently supported attributes are NumberFormatter::FRACTION_DIGITS,
551569
* NumberFormatter::GROUPING_USED and NumberFormatter::ROUNDING_MODE.
552-
* @param int $value The attribute value. The only currently supported rounding modes are
553-
* NumberFormatter::ROUND_HALFEVEN, NumberFormatter::ROUND_HALFDOWN and
554-
* NumberFormatter::ROUND_HALFUP.
570+
* @param int $value The attribute value.
555571
*
556572
* @return Boolean true on success or false on failure
557573
*
@@ -700,10 +716,32 @@ private function roundCurrency($value, $currency)
700716
*/
701717
private function round($value, $precision)
702718
{
703-
$precision = $this->getUnitializedPrecision($value, $precision);
719+
$precision = $this->getUninitializedPrecision($value, $precision);
720+
721+
$roundingModeAttribute = $this->getAttribute(self::ROUNDING_MODE);
722+
if (isset(self::$phpRoundingMap[$roundingModeAttribute])) {
723+
$value = round($value, $precision, self::$phpRoundingMap[$roundingModeAttribute]);
724+
} elseif (isset(self::$customRoundingList[$roundingModeAttribute])) {
725+
$roundingCoef = pow(10, $precision);
726+
$value *= $roundingCoef;
727+
728+
switch ($roundingModeAttribute) {
729+
case self::ROUND_CEILING:
730+
$value = ceil($value);
731+
break;
732+
case self::ROUND_FLOOR:
733+
$value = floor($value);
734+
break;
735+
case self::ROUND_UP:
736+
$value = $value > 0 ? ceil($value) : floor($value);
737+
break;
738+
case self::ROUND_DOWN:
739+
$value = $value > 0 ? floor($value) : ceil($value);
740+
break;
741+
}
704742

705-
$roundingMode = self::$phpRoundingMap[$this->getAttribute(self::ROUNDING_MODE)];
706-
$value = round($value, $precision, $roundingMode);
743+
$value /= $roundingCoef;
744+
}
707745

708746
return $value;
709747
}
@@ -718,20 +756,20 @@ private function round($value, $precision)
718756
*/
719757
private function formatNumber($value, $precision)
720758
{
721-
$precision = $this->getUnitializedPrecision($value, $precision);
759+
$precision = $this->getUninitializedPrecision($value, $precision);
722760

723761
return number_format($value, $precision, '.', $this->getAttribute(self::GROUPING_USED) ? ',' : '');
724762
}
725763

726764
/**
727-
* Returns the precision value if the DECIMAL style is being used and the FRACTION_DIGITS attribute is unitialized.
765+
* Returns the precision value if the DECIMAL style is being used and the FRACTION_DIGITS attribute is uninitialized.
728766
*
729-
* @param integer|float $value The value to get the precision from if the FRACTION_DIGITS attribute is unitialized
767+
* @param integer|float $value The value to get the precision from if the FRACTION_DIGITS attribute is uninitialized
730768
* @param int $precision The precision value to returns if the FRACTION_DIGITS attribute is initialized
731769
*
732770
* @return int The precision value
733771
*/
734-
private function getUnitializedPrecision($value, $precision)
772+
private function getUninitializedPrecision($value, $precision)
735773
{
736774
if ($this->style == self::CURRENCY) {
737775
return $precision;

src/Symfony/Component/Intl/Tests/NumberFormatter/AbstractNumberFormatterTest.php

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,102 @@ public function formatRoundingModeRoundHalfEvenProvider()
466466
);
467467
}
468468

469+
/**
470+
* @dataProvider formatRoundingModeRoundCeilingProvider
471+
*/
472+
public function testFormatRoundingModeCeiling($value, $expected)
473+
{
474+
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
475+
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
476+
477+
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_CEILING);
478+
$this->assertSame($expected, $formatter->format($value), '->format() with ROUND_CEILING rounding mode.');
479+
}
480+
481+
public function formatRoundingModeRoundCeilingProvider()
482+
{
483+
return array(
484+
array(1.123, '1.13'),
485+
array(1.125, '1.13'),
486+
array(1.127, '1.13'),
487+
array(-1.123, '-1.12'),
488+
array(-1.125, '-1.12'),
489+
array(-1.127, '-1.12'),
490+
);
491+
}
492+
493+
/**
494+
* @dataProvider formatRoundingModeRoundFloorProvider
495+
*/
496+
public function testFormatRoundingModeFloor($value, $expected)
497+
{
498+
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
499+
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
500+
501+
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_FLOOR);
502+
$this->assertSame($expected, $formatter->format($value), '->format() with ROUND_FLOOR rounding mode.');
503+
}
504+
505+
public function formatRoundingModeRoundFloorProvider()
506+
{
507+
return array(
508+
array(1.123, '1.12'),
509+
array(1.125, '1.12'),
510+
array(1.127, '1.12'),
511+
array(-1.123, '-1.13'),
512+
array(-1.125, '-1.13'),
513+
array(-1.127, '-1.13'),
514+
);
515+
}
516+
517+
/**
518+
* @dataProvider formatRoundingModeRoundDownProvider
519+
*/
520+
public function testFormatRoundingModeDown($value, $expected)
521+
{
522+
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
523+
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
524+
525+
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_DOWN);
526+
$this->assertSame($expected, $formatter->format($value), '->format() with ROUND_DOWN rounding mode.');
527+
}
528+
529+
public function formatRoundingModeRoundDownProvider()
530+
{
531+
return array(
532+
array(1.123, '1.12'),
533+
array(1.125, '1.12'),
534+
array(1.127, '1.12'),
535+
array(-1.123, '-1.12'),
536+
array(-1.125, '-1.12'),
537+
array(-1.127, '-1.12'),
538+
);
539+
}
540+
541+
/**
542+
* @dataProvider formatRoundingModeRoundUpProvider
543+
*/
544+
public function testFormatRoundingModeUp($value, $expected)
545+
{
546+
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);
547+
$formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, 2);
548+
549+
$formatter->setAttribute(NumberFormatter::ROUNDING_MODE, NumberFormatter::ROUND_UP);
550+
$this->assertSame($expected, $formatter->format($value), '->format() with ROUND_UP rounding mode.');
551+
}
552+
553+
public function formatRoundingModeRoundUpProvider()
554+
{
555+
return array(
556+
array(1.123, '1.13'),
557+
array(1.125, '1.13'),
558+
array(1.127, '1.13'),
559+
array(-1.123, '-1.13'),
560+
array(-1.125, '-1.13'),
561+
array(-1.127, '-1.13'),
562+
);
563+
}
564+
469565
public function testGetLocale()
470566
{
471567
$formatter = $this->getNumberFormatter('en', NumberFormatter::DECIMAL);

0 commit comments

Comments
 (0)
0