|
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
|
|
|
|