web/lib/Zend/Locale/Math.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     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 }