File indexing completed on 2024-12-22 05:36:50
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Locale 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 0023 /** 0024 * Utility class for proxying math function to bcmath functions, if present, 0025 * otherwise to PHP builtin math operators, with limited detection of overflow conditions. 0026 * Sampling of PHP environments and platforms suggests that at least 80% to 90% support bcmath. 0027 * Thus, this file should be as light as possible. 0028 * 0029 * @category Zend 0030 * @package Zend_Locale 0031 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0032 * @license http://framework.zend.com/license/new-bsd New BSD License 0033 */ 0034 0035 class Zend_Locale_Math 0036 { 0037 // support unit testing without using bcmath functions 0038 public static $_bcmathDisabled = false; 0039 0040 public static $add = array('Zend_Locale_Math', 'Add'); 0041 public static $sub = array('Zend_Locale_Math', 'Sub'); 0042 public static $pow = array('Zend_Locale_Math', 'Pow'); 0043 public static $mul = array('Zend_Locale_Math', 'Mul'); 0044 public static $div = array('Zend_Locale_Math', 'Div'); 0045 public static $comp = array('Zend_Locale_Math', 'Comp'); 0046 public static $sqrt = array('Zend_Locale_Math', 'Sqrt'); 0047 public static $mod = array('Zend_Locale_Math', 'Mod'); 0048 public static $scale = 'bcscale'; 0049 0050 public static function isBcmathDisabled() 0051 { 0052 return self::$_bcmathDisabled; 0053 } 0054 0055 /** 0056 * Surprisingly, the results of this implementation of round() 0057 * prove better than the native PHP round(). For example, try: 0058 * round(639.795, 2); 0059 * round(267.835, 2); 0060 * round(0.302515, 5); 0061 * round(0.36665, 4); 0062 * then try: 0063 * Zend_Locale_Math::round('639.795', 2); 0064 */ 0065 public static function round($op1, $precision = 0) 0066 { 0067 if (self::$_bcmathDisabled) { 0068 $op1 = round($op1, $precision); 0069 if (strpos((string) $op1, 'E') === false) { 0070 return self::normalize(round($op1, $precision)); 0071 } 0072 } 0073 0074 if (strpos($op1, 'E') !== false) { 0075 $op1 = self::floatalize($op1); 0076 } 0077 0078 $op1 = trim(self::normalize($op1)); 0079 $length = strlen($op1); 0080 if (($decPos = strpos($op1, '.')) === false) { 0081 $op1 .= '.0'; 0082 $decPos = $length; 0083 $length += 2; 0084 } 0085 if ($precision < 0 && abs($precision) > $decPos) { 0086 return '0'; 0087 } 0088 0089 $digitsBeforeDot = $length - ($decPos + 1); 0090 if ($precision >= ($length - ($decPos + 1))) { 0091 return $op1; 0092 } 0093 0094 if ($precision === 0) { 0095 $triggerPos = 1; 0096 $roundPos = -1; 0097 } elseif ($precision > 0) { 0098 $triggerPos = $precision + 1; 0099 $roundPos = $precision; 0100 } else { 0101 $triggerPos = $precision; 0102 $roundPos = $precision -1; 0103 } 0104 0105 $triggerDigit = $op1[$triggerPos + $decPos]; 0106 if ($precision < 0) { 0107 // zero fill digits to the left of the decimal place 0108 $op1 = substr($op1, 0, $decPos + $precision) . str_pad('', abs($precision), '0'); 0109 } 0110 0111 if ($triggerDigit >= '5') { 0112 if ($roundPos + $decPos == -1) { 0113 return str_pad('1', $decPos + 1, '0'); 0114 } 0115 0116 $roundUp = str_pad('', $length, '0'); 0117 $roundUp[$decPos] = '.'; 0118 $roundUp[$roundPos + $decPos] = '1'; 0119 0120 if ($op1 > 0) { 0121 if (self::$_bcmathDisabled) { 0122 return Zend_Locale_Math_PhpMath::Add($op1, $roundUp, $precision); 0123 } 0124 return self::Add($op1, $roundUp, $precision); 0125 } else { 0126 if (self::$_bcmathDisabled) { 0127 return Zend_Locale_Math_PhpMath::Sub($op1, $roundUp, $precision); 0128 } 0129 return self::Sub($op1, $roundUp, $precision); 0130 } 0131 } elseif ($precision >= 0) { 0132 return substr($op1, 0, $decPos + ($precision ? $precision + 1: 0)); 0133 } 0134 0135 return (string) $op1; 0136 } 0137 0138 /** 0139 * Convert a scientific notation to float 0140 * Additionally fixed a problem with PHP <= 5.2.x with big integers 0141 * 0142 * @param string $value 0143 */ 0144 public static function floatalize($value) 0145 { 0146 $value = strtoupper($value); 0147 if (strpos($value, 'E') === false) { 0148 return $value; 0149 } 0150 0151 $number = substr($value, 0, strpos($value, 'E')); 0152 if (strpos($number, '.') !== false) { 0153 $post = strlen(substr($number, strpos($number, '.') + 1)); 0154 $mantis = substr($value, strpos($value, 'E') + 1); 0155 if ($mantis < 0) { 0156 $post += abs((int) $mantis); 0157 } 0158 0159 $value = number_format($value, $post, '.', ''); 0160 } else { 0161 $value = number_format($value, 0, '.', ''); 0162 } 0163 0164 return $value; 0165 } 0166 0167 /** 0168 * Normalizes an input to standard english notation 0169 * Fixes a problem of BCMath with setLocale which is PHP related 0170 * 0171 * @param integer $value Value to normalize 0172 * @return string Normalized string without BCMath problems 0173 */ 0174 public static function normalize($value) 0175 { 0176 $convert = localeconv(); 0177 $value = str_replace($convert['thousands_sep'], "",(string) $value); 0178 $value = str_replace($convert['positive_sign'], "", $value); 0179 $value = str_replace($convert['decimal_point'], ".",$value); 0180 if (!empty($convert['negative_sign']) and (strpos($value, $convert['negative_sign']))) { 0181 $value = str_replace($convert['negative_sign'], "", $value); 0182 $value = "-" . $value; 0183 } 0184 0185 return $value; 0186 } 0187 0188 /** 0189 * Localizes an input from standard english notation 0190 * Fixes a problem of BCMath with setLocale which is PHP related 0191 * 0192 * @param integer $value Value to normalize 0193 * @return string Normalized string without BCMath problems 0194 */ 0195 public static function localize($value) 0196 { 0197 $convert = localeconv(); 0198 $value = str_replace(".", $convert['decimal_point'], (string) $value); 0199 if (!empty($convert['negative_sign']) and (strpos($value, "-"))) { 0200 $value = str_replace("-", $convert['negative_sign'], $value); 0201 } 0202 return $value; 0203 } 0204 0205 /** 0206 * Changes exponential numbers to plain string numbers 0207 * Fixes a problem of BCMath with numbers containing exponents 0208 * 0209 * @param integer $value Value to erase the exponent 0210 * @param integer $scale (Optional) Scale to use 0211 * @return string 0212 */ 0213 public static function exponent($value, $scale = null) 0214 { 0215 if (!extension_loaded('bcmath')) { 0216 return $value; 0217 } 0218 0219 $split = explode('e', $value); 0220 if (count($split) == 1) { 0221 $split = explode('E', $value); 0222 } 0223 0224 if (count($split) > 1) { 0225 $value = bcmul($split[0], bcpow(10, $split[1], $scale), $scale); 0226 } 0227 0228 return $value; 0229 } 0230 0231 /** 0232 * BCAdd - fixes a problem of BCMath and exponential numbers 0233 * 0234 * @param string $op1 0235 * @param string $op2 0236 * @param integer $scale 0237 * @return string 0238 */ 0239 public static function Add($op1, $op2, $scale = null) 0240 { 0241 $op1 = self::exponent($op1, $scale); 0242 $op2 = self::exponent($op2, $scale); 0243 0244 return bcadd($op1, $op2, $scale); 0245 } 0246 0247 /** 0248 * BCSub - fixes a problem of BCMath and exponential numbers 0249 * 0250 * @param string $op1 0251 * @param string $op2 0252 * @param integer $scale 0253 * @return string 0254 */ 0255 public static function Sub($op1, $op2, $scale = null) 0256 { 0257 $op1 = self::exponent($op1, $scale); 0258 $op2 = self::exponent($op2, $scale); 0259 return bcsub($op1, $op2, $scale); 0260 } 0261 0262 /** 0263 * BCPow - fixes a problem of BCMath and exponential numbers 0264 * 0265 * @param string $op1 0266 * @param string $op2 0267 * @param integer $scale 0268 * @return string 0269 */ 0270 public static function Pow($op1, $op2, $scale = null) 0271 { 0272 $op1 = self::exponent($op1, $scale); 0273 $op2 = self::exponent($op2, $scale); 0274 return bcpow($op1, $op2, $scale); 0275 } 0276 0277 /** 0278 * BCMul - fixes a problem of BCMath and exponential numbers 0279 * 0280 * @param string $op1 0281 * @param string $op2 0282 * @param integer $scale 0283 * @return string 0284 */ 0285 public static function Mul($op1, $op2, $scale = null) 0286 { 0287 $op1 = self::exponent($op1, $scale); 0288 $op2 = self::exponent($op2, $scale); 0289 return bcmul($op1, $op2, $scale); 0290 } 0291 0292 /** 0293 * BCDiv - fixes a problem of BCMath and exponential numbers 0294 * 0295 * @param string $op1 0296 * @param string $op2 0297 * @param integer $scale 0298 * @return string 0299 */ 0300 public static function Div($op1, $op2, $scale = null) 0301 { 0302 $op1 = self::exponent($op1, $scale); 0303 $op2 = self::exponent($op2, $scale); 0304 return bcdiv($op1, $op2, $scale); 0305 } 0306 0307 /** 0308 * BCSqrt - fixes a problem of BCMath and exponential numbers 0309 * 0310 * @param string $op1 0311 * @param integer $scale 0312 * @return string 0313 */ 0314 public static function Sqrt($op1, $scale = null) 0315 { 0316 $op1 = self::exponent($op1, $scale); 0317 return bcsqrt($op1, $scale); 0318 } 0319 0320 /** 0321 * BCMod - fixes a problem of BCMath and exponential numbers 0322 * 0323 * @param string $op1 0324 * @param string $op2 0325 * @return string 0326 */ 0327 public static function Mod($op1, $op2) 0328 { 0329 $op1 = self::exponent($op1); 0330 $op2 = self::exponent($op2); 0331 return bcmod($op1, $op2); 0332 } 0333 0334 /** 0335 * BCComp - fixes a problem of BCMath and exponential numbers 0336 * 0337 * @param string $op1 0338 * @param string $op2 0339 * @param integer $scale 0340 * @return string 0341 */ 0342 public static function Comp($op1, $op2, $scale = null) 0343 { 0344 $op1 = self::exponent($op1, $scale); 0345 $op2 = self::exponent($op2, $scale); 0346 return bccomp($op1, $op2, $scale); 0347 } 0348 } 0349 0350 if (!extension_loaded('bcmath') 0351 || (defined('TESTS_ZEND_LOCALE_BCMATH_ENABLED') && !TESTS_ZEND_LOCALE_BCMATH_ENABLED) 0352 ) { 0353 // require_once 'Zend/Locale/Math/PhpMath.php'; 0354 Zend_Locale_Math_PhpMath::disable(); 0355 }