|
1 <?php |
|
2 /** |
|
3 * Zend Framework |
|
4 * |
|
5 * LICENSE |
|
6 * |
|
7 * This source file is subject to the new BSD license that is bundled |
|
8 * with this package in the file LICENSE.txt. |
|
9 * It is also available through the world-wide-web at this URL: |
|
10 * http://framework.zend.com/license/new-bsd |
|
11 * If you did not receive a copy of the license and are unable to |
|
12 * obtain it through the world-wide-web, please send an email |
|
13 * to license@zend.com so we can send you a copy immediately. |
|
14 * |
|
15 * @category Zend |
|
16 * @package Zend_Locale |
|
17 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
18 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
19 * @version $Id: Math.php 21180 2010-02-23 22:00:29Z matthew $ |
|
20 */ |
|
21 |
|
22 |
|
23 /** |
|
24 * Utility class for proxying math function to bcmath functions, if present, |
|
25 * otherwise to PHP builtin math operators, with limited detection of overflow conditions. |
|
26 * Sampling of PHP environments and platforms suggests that at least 80% to 90% support bcmath. |
|
27 * Thus, this file should be as light as possible. |
|
28 * |
|
29 * @category Zend |
|
30 * @package Zend_Locale |
|
31 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) |
|
32 * @license http://framework.zend.com/license/new-bsd New BSD License |
|
33 */ |
|
34 |
|
35 class Zend_Locale_Math |
|
36 { |
|
37 // support unit testing without using bcmath functions |
|
38 public static $_bcmathDisabled = false; |
|
39 |
|
40 public static $add = array('Zend_Locale_Math', 'Add'); |
|
41 public static $sub = array('Zend_Locale_Math', 'Sub'); |
|
42 public static $pow = array('Zend_Locale_Math', 'Pow'); |
|
43 public static $mul = array('Zend_Locale_Math', 'Mul'); |
|
44 public static $div = array('Zend_Locale_Math', 'Div'); |
|
45 public static $comp = array('Zend_Locale_Math', 'Comp'); |
|
46 public static $sqrt = array('Zend_Locale_Math', 'Sqrt'); |
|
47 public static $mod = array('Zend_Locale_Math', 'Mod'); |
|
48 public static $scale = 'bcscale'; |
|
49 |
|
50 public static function isBcmathDisabled() |
|
51 { |
|
52 return self::$_bcmathDisabled; |
|
53 } |
|
54 |
|
55 /** |
|
56 * Surprisingly, the results of this implementation of round() |
|
57 * prove better than the native PHP round(). For example, try: |
|
58 * round(639.795, 2); |
|
59 * round(267.835, 2); |
|
60 * round(0.302515, 5); |
|
61 * round(0.36665, 4); |
|
62 * then try: |
|
63 * Zend_Locale_Math::round('639.795', 2); |
|
64 */ |
|
65 public static function round($op1, $precision = 0) |
|
66 { |
|
67 if (self::$_bcmathDisabled) { |
|
68 $op1 = round($op1, $precision); |
|
69 if (strpos((string) $op1, 'E') === false) { |
|
70 return self::normalize(round($op1, $precision)); |
|
71 } |
|
72 } |
|
73 |
|
74 if (strpos($op1, 'E') !== false) { |
|
75 $op1 = self::floatalize($op1); |
|
76 } |
|
77 |
|
78 $op1 = trim(self::normalize($op1)); |
|
79 $length = strlen($op1); |
|
80 if (($decPos = strpos($op1, '.')) === false) { |
|
81 $op1 .= '.0'; |
|
82 $decPos = $length; |
|
83 $length += 2; |
|
84 } |
|
85 if ($precision < 0 && abs($precision) > $decPos) { |
|
86 return '0'; |
|
87 } |
|
88 |
|
89 $digitsBeforeDot = $length - ($decPos + 1); |
|
90 if ($precision >= ($length - ($decPos + 1))) { |
|
91 return $op1; |
|
92 } |
|
93 |
|
94 if ($precision === 0) { |
|
95 $triggerPos = 1; |
|
96 $roundPos = -1; |
|
97 } elseif ($precision > 0) { |
|
98 $triggerPos = $precision + 1; |
|
99 $roundPos = $precision; |
|
100 } else { |
|
101 $triggerPos = $precision; |
|
102 $roundPos = $precision -1; |
|
103 } |
|
104 |
|
105 $triggerDigit = $op1[$triggerPos + $decPos]; |
|
106 if ($precision < 0) { |
|
107 // zero fill digits to the left of the decimal place |
|
108 $op1 = substr($op1, 0, $decPos + $precision) . str_pad('', abs($precision), '0'); |
|
109 } |
|
110 |
|
111 if ($triggerDigit >= '5') { |
|
112 if ($roundPos + $decPos == -1) { |
|
113 return str_pad('1', $decPos + 1, '0'); |
|
114 } |
|
115 |
|
116 $roundUp = str_pad('', $length, '0'); |
|
117 $roundUp[$decPos] = '.'; |
|
118 $roundUp[$roundPos + $decPos] = '1'; |
|
119 |
|
120 if ($op1 > 0) { |
|
121 if (self::$_bcmathDisabled) { |
|
122 return Zend_Locale_Math_PhpMath::Add($op1, $roundUp, $precision); |
|
123 } |
|
124 return self::Add($op1, $roundUp, $precision); |
|
125 } else { |
|
126 if (self::$_bcmathDisabled) { |
|
127 return Zend_Locale_Math_PhpMath::Sub($op1, $roundUp, $precision); |
|
128 } |
|
129 return self::Sub($op1, $roundUp, $precision); |
|
130 } |
|
131 } elseif ($precision >= 0) { |
|
132 return substr($op1, 0, $decPos + ($precision ? $precision + 1: 0)); |
|
133 } |
|
134 |
|
135 return (string) $op1; |
|
136 } |
|
137 |
|
138 /** |
|
139 * Convert a scientific notation to float |
|
140 * Additionally fixed a problem with PHP <= 5.2.x with big integers |
|
141 * |
|
142 * @param string $value |
|
143 */ |
|
144 public static function floatalize($value) |
|
145 { |
|
146 $value = strtoupper($value); |
|
147 if (strpos($value, 'E') === false) { |
|
148 return $value; |
|
149 } |
|
150 |
|
151 $number = substr($value, 0, strpos($value, 'E')); |
|
152 if (strpos($number, '.') !== false) { |
|
153 $post = strlen(substr($number, strpos($number, '.') + 1)); |
|
154 $mantis = substr($value, strpos($value, 'E') + 1); |
|
155 if ($mantis < 0) { |
|
156 $post += abs((int) $mantis); |
|
157 } |
|
158 |
|
159 $value = number_format($value, $post, '.', ''); |
|
160 } else { |
|
161 $value = number_format($value, 0, '.', ''); |
|
162 } |
|
163 |
|
164 return $value; |
|
165 } |
|
166 |
|
167 /** |
|
168 * Normalizes an input to standard english notation |
|
169 * Fixes a problem of BCMath with setLocale which is PHP related |
|
170 * |
|
171 * @param integer $value Value to normalize |
|
172 * @return string Normalized string without BCMath problems |
|
173 */ |
|
174 public static function normalize($value) |
|
175 { |
|
176 $convert = localeconv(); |
|
177 $value = str_replace($convert['thousands_sep'], "",(string) $value); |
|
178 $value = str_replace($convert['positive_sign'], "", $value); |
|
179 $value = str_replace($convert['decimal_point'], ".",$value); |
|
180 if (!empty($convert['negative_sign']) and (strpos($value, $convert['negative_sign']))) { |
|
181 $value = str_replace($convert['negative_sign'], "", $value); |
|
182 $value = "-" . $value; |
|
183 } |
|
184 |
|
185 return $value; |
|
186 } |
|
187 |
|
188 /** |
|
189 * Localizes an input from standard english notation |
|
190 * Fixes a problem of BCMath with setLocale which is PHP related |
|
191 * |
|
192 * @param integer $value Value to normalize |
|
193 * @return string Normalized string without BCMath problems |
|
194 */ |
|
195 public static function localize($value) |
|
196 { |
|
197 $convert = localeconv(); |
|
198 $value = str_replace(".", $convert['decimal_point'], (string) $value); |
|
199 if (!empty($convert['negative_sign']) and (strpos($value, "-"))) { |
|
200 $value = str_replace("-", $convert['negative_sign'], $value); |
|
201 } |
|
202 return $value; |
|
203 } |
|
204 |
|
205 /** |
|
206 * Changes exponential numbers to plain string numbers |
|
207 * Fixes a problem of BCMath with numbers containing exponents |
|
208 * |
|
209 * @param integer $value Value to erase the exponent |
|
210 * @param integer $scale (Optional) Scale to use |
|
211 * @return string |
|
212 */ |
|
213 public static function exponent($value, $scale = null) |
|
214 { |
|
215 if (!extension_loaded('bcmath')) { |
|
216 return $value; |
|
217 } |
|
218 |
|
219 $split = explode('e', $value); |
|
220 if (count($split) == 1) { |
|
221 $split = explode('E', $value); |
|
222 } |
|
223 |
|
224 if (count($split) > 1) { |
|
225 $value = bcmul($split[0], bcpow(10, $split[1], $scale), $scale); |
|
226 } |
|
227 |
|
228 return $value; |
|
229 } |
|
230 |
|
231 /** |
|
232 * BCAdd - fixes a problem of BCMath and exponential numbers |
|
233 * |
|
234 * @param string $op1 |
|
235 * @param string $op2 |
|
236 * @param integer $scale |
|
237 * @return string |
|
238 */ |
|
239 public static function Add($op1, $op2, $scale = null) |
|
240 { |
|
241 $op1 = self::exponent($op1, $scale); |
|
242 $op2 = self::exponent($op2, $scale); |
|
243 |
|
244 return bcadd($op1, $op2, $scale); |
|
245 } |
|
246 |
|
247 /** |
|
248 * BCSub - fixes a problem of BCMath and exponential numbers |
|
249 * |
|
250 * @param string $op1 |
|
251 * @param string $op2 |
|
252 * @param integer $scale |
|
253 * @return string |
|
254 */ |
|
255 public static function Sub($op1, $op2, $scale = null) |
|
256 { |
|
257 $op1 = self::exponent($op1, $scale); |
|
258 $op2 = self::exponent($op2, $scale); |
|
259 return bcsub($op1, $op2, $scale); |
|
260 } |
|
261 |
|
262 /** |
|
263 * BCPow - fixes a problem of BCMath and exponential numbers |
|
264 * |
|
265 * @param string $op1 |
|
266 * @param string $op2 |
|
267 * @param integer $scale |
|
268 * @return string |
|
269 */ |
|
270 public static function Pow($op1, $op2, $scale = null) |
|
271 { |
|
272 $op1 = self::exponent($op1, $scale); |
|
273 $op2 = self::exponent($op2, $scale); |
|
274 return bcpow($op1, $op2, $scale); |
|
275 } |
|
276 |
|
277 /** |
|
278 * BCMul - fixes a problem of BCMath and exponential numbers |
|
279 * |
|
280 * @param string $op1 |
|
281 * @param string $op2 |
|
282 * @param integer $scale |
|
283 * @return string |
|
284 */ |
|
285 public static function Mul($op1, $op2, $scale = null) |
|
286 { |
|
287 $op1 = self::exponent($op1, $scale); |
|
288 $op2 = self::exponent($op2, $scale); |
|
289 return bcmul($op1, $op2, $scale); |
|
290 } |
|
291 |
|
292 /** |
|
293 * BCDiv - fixes a problem of BCMath and exponential numbers |
|
294 * |
|
295 * @param string $op1 |
|
296 * @param string $op2 |
|
297 * @param integer $scale |
|
298 * @return string |
|
299 */ |
|
300 public static function Div($op1, $op2, $scale = null) |
|
301 { |
|
302 $op1 = self::exponent($op1, $scale); |
|
303 $op2 = self::exponent($op2, $scale); |
|
304 return bcdiv($op1, $op2, $scale); |
|
305 } |
|
306 |
|
307 /** |
|
308 * BCSqrt - fixes a problem of BCMath and exponential numbers |
|
309 * |
|
310 * @param string $op1 |
|
311 * @param integer $scale |
|
312 * @return string |
|
313 */ |
|
314 public static function Sqrt($op1, $scale = null) |
|
315 { |
|
316 $op1 = self::exponent($op1, $scale); |
|
317 return bcsqrt($op1, $scale); |
|
318 } |
|
319 |
|
320 /** |
|
321 * BCMod - fixes a problem of BCMath and exponential numbers |
|
322 * |
|
323 * @param string $op1 |
|
324 * @param string $op2 |
|
325 * @return string |
|
326 */ |
|
327 public static function Mod($op1, $op2) |
|
328 { |
|
329 $op1 = self::exponent($op1); |
|
330 $op2 = self::exponent($op2); |
|
331 return bcmod($op1, $op2); |
|
332 } |
|
333 |
|
334 /** |
|
335 * BCComp - fixes a problem of BCMath and exponential numbers |
|
336 * |
|
337 * @param string $op1 |
|
338 * @param string $op2 |
|
339 * @param integer $scale |
|
340 * @return string |
|
341 */ |
|
342 public static function Comp($op1, $op2, $scale = null) |
|
343 { |
|
344 $op1 = self::exponent($op1, $scale); |
|
345 $op2 = self::exponent($op2, $scale); |
|
346 return bccomp($op1, $op2, $scale); |
|
347 } |
|
348 } |
|
349 |
|
350 if (!extension_loaded('bcmath') |
|
351 || (defined('TESTS_ZEND_LOCALE_BCMATH_ENABLED') && !TESTS_ZEND_LOCALE_BCMATH_ENABLED) |
|
352 ) { |
|
353 require_once 'Zend/Locale/Math/PhpMath.php'; |
|
354 Zend_Locale_Math_PhpMath::disable(); |
|
355 } |