1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Fhferreira\Round; |
6
|
|
|
|
7
|
|
|
use Fhferreira\Round\Exceptions\CommaException; |
8
|
|
|
|
9
|
|
|
/** |
10
|
|
|
* Class Rounder |
11
|
|
|
* @package Fhferreira\Round |
12
|
|
|
* |
13
|
|
|
* The rounding rules, following the ABNT NBR 5891 Standard, apply to the decimal digits located in the position |
14
|
|
|
* following the number of decimal digits to be transformed, that is, if we have a number of 4, 5, 6, n decimal digits |
15
|
|
|
* and if we want to round to 2, these rounding rules will apply: |
16
|
|
|
* |
17
|
|
|
* If the following decimal digits are less than 50, 500, 5000 ..., the previous one does not change. |
18
|
|
|
* If the following decimal digits are greater than 50, 500, 5000 ..., the previous one is incremented by one. |
19
|
|
|
* If the following decimal digits are equal to 50, 500, 5000 ..., the former is verified; if it is even, the former |
20
|
|
|
* does not change; if it is odd, the former is increased by one. |
21
|
|
|
* |
22
|
|
|
* Examples Edit |
23
|
|
|
* Rounding to 2 decimal digits, we must pay attention to the third and fourth decimal. Thus, according to the previous |
24
|
|
|
* rules: The number 12.6529 would be rounded up to 12.65 (here it is 12.65, since 29 is less than 50, so it does not |
25
|
|
|
* change) The number 12.86512 would be rounded to 12.87 (here it is 12.87, since 512 is greater than 500, then a unit |
26
|
|
|
* is increased) The number 12.744623 would be rounded to 12.74 (here it is 12.74, since 4623 is less than 5000, so it |
27
|
|
|
* does not change) The number 12.8752 would be rounded to 12.88 (here it is 12.88, since 52 is greater than 50, then a |
28
|
|
|
* unit is increased) The number 12.8150 would be rounded to 12.82 (here it is 12.82, since the next digits are equal |
29
|
|
|
* to 50 and the previous one is odd, in this case 1, then a unit is increased) The number 12.8050 would be rounded to |
30
|
|
|
* 12.80 (here it is 12.80, since the following digits are equal to 50 and the previous one is even, in this case 0, |
31
|
|
|
* then the previous one does not change) |
32
|
|
|
*/ |
33
|
|
|
class AbntNbr5891 |
34
|
|
|
{ |
35
|
|
|
/** Round number using brazilian normative ABNT NBR 5891 |
36
|
|
|
* |
37
|
|
|
* @param $number |
38
|
|
|
* |
39
|
|
|
* @return mixed |
40
|
|
|
* @throws CommaException |
41
|
|
|
*/ |
42
|
|
|
public static function round($number) |
43
|
|
|
{ |
44
|
|
|
$number = (string)$number; |
45
|
|
|
|
46
|
|
|
if (strpos($number, ',') !== false) { |
47
|
|
|
throw new CommaException(); |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
$numberArr = explode('.', "" . $number); |
51
|
|
|
|
52
|
|
|
$intPart = $numberArr[0]; |
53
|
|
|
$decimalsNumbers = isset($numberArr[1]) ? (string)$numberArr[1] : '00'; |
54
|
|
|
|
55
|
|
|
$down = false; |
56
|
|
|
$up = false; |
57
|
|
|
|
58
|
|
|
if (strlen($decimalsNumbers) <= 2) { |
59
|
|
|
$decimalsNumbers = str_pad($decimalsNumbers, 2, '0', STR_PAD_RIGHT); |
60
|
|
|
return ((float)($intPart . '.' . $decimalsNumbers)); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
$decimalsStr = substr($decimalsNumbers, 0, 2); |
64
|
|
|
$restStr = substr($decimalsNumbers, 2); |
65
|
|
|
|
66
|
|
|
if (strlen($restStr) == 1) { |
67
|
|
|
$restStr = $restStr . '0'; |
68
|
|
|
} |
69
|
|
|
|
70
|
|
|
$strlenRest = strlen($restStr); |
71
|
|
|
$finalRest = str_pad('5', $strlenRest, '0', STR_PAD_RIGHT); |
72
|
|
|
|
73
|
|
|
if ($restStr < $finalRest) { |
74
|
|
|
$down = true; |
75
|
|
|
$up = false; |
76
|
|
|
} else { |
77
|
|
|
if ($restStr > $finalRest) { |
78
|
|
|
$down = false; |
79
|
|
|
$up = true; |
80
|
|
|
} else { |
81
|
|
|
if ($restStr == $finalRest) { |
82
|
|
|
if (((int)$decimalsStr[1]) % 2 == 1) { |
83
|
|
|
$down = false; |
84
|
|
|
$up = true; |
85
|
|
|
} else { |
86
|
|
|
$down = true; |
87
|
|
|
$up = false; |
88
|
|
|
} |
89
|
|
|
} |
90
|
|
|
} |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
$final = 0; |
94
|
|
|
if ($down) { |
95
|
|
|
$final = ((float)($intPart . '.' . $decimalsStr)); |
96
|
|
|
} else { |
97
|
|
|
if ($up) { |
98
|
|
|
$decimals = $decimalsStr + 1; |
99
|
|
|
$sumInt = $decimals >= 100 ? 1 : 0; |
100
|
|
|
if ($sumInt > 0 && $decimals == 100) { |
101
|
|
|
$decimals = '00'; |
102
|
|
|
} else if ($sumInt > 0 && $decimals > 100) { |
103
|
|
|
$decimals = $decimals - 100; |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
if ($decimalsStr[0] == 0 && $decimals < 10) { |
107
|
|
|
$decimals = '0' . $decimals; |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
$final = ((float)(($intPart + $sumInt) . '.' . ($decimals))); |
111
|
|
|
|
112
|
|
|
if ($intPart === '-0') { |
113
|
|
|
$final *= -1; |
114
|
|
|
} |
115
|
|
|
} |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
return $final; |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* @param $value |
123
|
|
|
* @param int $percent |
124
|
|
|
* @param bool $rounded |
125
|
|
|
* |
126
|
|
|
* @return float |
127
|
|
|
*/ |
128
|
|
|
public static function getPercentFromAmount($value, $percent = 100): float |
129
|
|
|
{ |
130
|
|
|
return (float) ($value / 100 * $percent); |
131
|
|
|
} |
132
|
|
|
} |
133
|
|
|
|