<?php /** * Zend Framework * * LICENSE * * This source file is subject to the new BSD license that is bundled * with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://framework.zend.com/license/new-bsd * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to [email protected] so we can send you a copy immediately. * * @category Zend * @package Zend_Locale * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @version $Id$ */ /** * Utility class for proxying math function to bcmath functions, if present, * otherwise to PHP builtin math operators, with limited detection of overflow conditions. * Sampling of PHP environments and platforms suggests that at least 80% to 90% support bcmath. * Thus, this file should be as light as possible. * * @category Zend * @package Zend_Locale * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Locale_Math { // support unit testing without using bcmath functions public static $_bcmathDisabled = false; public static $add = array('Zend_Locale_Math', 'Add'); public static $sub = array('Zend_Locale_Math', 'Sub'); public static $pow = array('Zend_Locale_Math', 'Pow'); public static $mul = array('Zend_Locale_Math', 'Mul'); public static $div = array('Zend_Locale_Math', 'Div'); public static $comp = array('Zend_Locale_Math', 'Comp'); public static $sqrt = array('Zend_Locale_Math', 'Sqrt'); public static $mod = array('Zend_Locale_Math', 'Mod'); public static $scale = 'bcscale'; public static function isBcmathDisabled() { return self::$_bcmathDisabled; } /** * Surprisingly, the results of this implementation of round() * prove better than the native PHP round(). For example, try: * round(639.795, 2); * round(267.835, 2); * round(0.302515, 5); * round(0.36665, 4); * then try: * Zend_Locale_Math::round('639.795', 2); */ public static function round($op1, $precision = 0) { if (self::$_bcmathDisabled) { $op1 = round($op1, $precision); if (strpos((string) $op1, 'E') === false) { return self::normalize(round($op1, $precision)); } } if (strpos($op1, 'E') !== false) { $op1 = self::floatalize($op1); } $op1 = trim(self::normalize($op1)); $length = strlen($op1); if (($decPos = strpos($op1, '.')) === false) { $op1 .= '.0'; $decPos = $length; $length += 2; } if ($precision < 0 && abs($precision) > $decPos) { return '0'; } $digitsBeforeDot = $length - ($decPos + 1); if ($precision >= ($length - ($decPos + 1))) { return $op1; } if ($precision === 0) { $triggerPos = 1; $roundPos = -1; } elseif ($precision > 0) { $triggerPos = $precision + 1; $roundPos = $precision; } else { $triggerPos = $precision; $roundPos = $precision -1; } $triggerDigit = $op1[$triggerPos + $decPos]; if ($precision < 0) { // zero fill digits to the left of the decimal place $op1 = substr($op1, 0, $decPos + $precision) . str_pad('', abs($precision), '0'); } if ($triggerDigit >= '5') { if ($roundPos + $decPos == -1) { return str_pad('1', $decPos + 1, '0'); } $roundUp = str_pad('', $length, '0'); $roundUp[$decPos] = '.'; $roundUp[$roundPos + $decPos] = '1'; if ($op1 > 0) { if (self::$_bcmathDisabled) { return Zend_Locale_Math_PhpMath::Add($op1, $roundUp, $precision); } return self::Add($op1, $roundUp, $precision); } else { if (self::$_bcmathDisabled) { return Zend_Locale_Math_PhpMath::Sub($op1, $roundUp, $precision); } return self::Sub($op1, $roundUp, $precision); } } elseif ($precision >= 0) { return substr($op1, 0, $decPos + ($precision ? $precision + 1: 0)); } return (string) $op1; } /** * Convert a scientific notation to float * Additionally fixed a problem with PHP <= 5.2.x with big integers * * @param string $value */ public static function floatalize($value) { $value = strtoupper($value); if (strpos($value, 'E') === false) { return $value; } $number = substr($value, 0, strpos($value, 'E')); if (strpos($number, '.') !== false) { $post = strlen(substr($number, strpos($number, '.') + 1)); $mantis = substr($value, strpos($value, 'E') + 1); if ($mantis < 0) { $post += abs((int) $mantis); } $value = number_format($value, $post, '.', ''); } else { $value = number_format($value, 0, '.', ''); } return $value; } /** * Normalizes an input to standard english notation * Fixes a problem of BCMath with setLocale which is PHP related * * @param integer $value Value to normalize * @return string Normalized string without BCMath problems */ public static function normalize($value) { $convert = localeconv(); $value = str_replace($convert['thousands_sep'], "",(string) $value); $value = str_replace($convert['positive_sign'], "", $value); $value = str_replace($convert['decimal_point'], ".",$value); if (!empty($convert['negative_sign']) and (strpos($value, $convert['negative_sign']))) { $value = str_replace($convert['negative_sign'], "", $value); $value = "-" . $value; } return $value; } /** * Localizes an input from standard english notation * Fixes a problem of BCMath with setLocale which is PHP related * * @param integer $value Value to normalize * @return string Normalized string without BCMath problems */ public static function localize($value) { $convert = localeconv(); $value = str_replace(".", $convert['decimal_point'], (string) $value); if (!empty($convert['negative_sign']) and (strpos($value, "-"))) { $value = str_replace("-", $convert['negative_sign'], $value); } return $value; } /** * Changes exponential numbers to plain string numbers * Fixes a problem of BCMath with numbers containing exponents * * @param integer $value Value to erase the exponent * @param integer $scale (Optional) Scale to use * @return string */ public static function exponent($value, $scale = null) { if (!extension_loaded('bcmath')) { return $value; } $split = explode('e', $value); if (count($split) == 1) { $split = explode('E', $value); } if (count($split) > 1) { $value = bcmul($split[0], bcpow(10, $split[1], $scale), $scale); } return $value; } /** * BCAdd - fixes a problem of BCMath and exponential numbers * * @param string $op1 * @param string $op2 * @param integer $scale * @return string */ public static function Add($op1, $op2, $scale = null) { $op1 = self::exponent($op1, $scale); $op2 = self::exponent($op2, $scale); return bcadd($op1, $op2, $scale); } /** * BCSub - fixes a problem of BCMath and exponential numbers * * @param string $op1 * @param string $op2 * @param integer $scale * @return string */ public static function Sub($op1, $op2, $scale = null) { $op1 = self::exponent($op1, $scale); $op2 = self::exponent($op2, $scale); return bcsub($op1, $op2, $scale); } /** * BCPow - fixes a problem of BCMath and exponential numbers * * @param string $op1 * @param string $op2 * @param integer $scale * @return string */ public static function Pow($op1, $op2, $scale = null) { $op1 = self::exponent($op1, $scale); $op2 = self::exponent($op2, $scale); return bcpow($op1, $op2, $scale); } /** * BCMul - fixes a problem of BCMath and exponential numbers * * @param string $op1 * @param string $op2 * @param integer $scale * @return string */ public static function Mul($op1, $op2, $scale = null) { $op1 = self::exponent($op1, $scale); $op2 = self::exponent($op2, $scale); return bcmul($op1, $op2, $scale); } /** * BCDiv - fixes a problem of BCMath and exponential numbers * * @param string $op1 * @param string $op2 * @param integer $scale * @return string */ public static function Div($op1, $op2, $scale = null) { $op1 = self::exponent($op1, $scale); $op2 = self::exponent($op2, $scale); return bcdiv($op1, $op2, $scale); } /** * BCSqrt - fixes a problem of BCMath and exponential numbers * * @param string $op1 * @param integer $scale * @return string */ public static function Sqrt($op1, $scale = null) { $op1 = self::exponent($op1, $scale); return bcsqrt($op1, $scale); } /** * BCMod - fixes a problem of BCMath and exponential numbers * * @param string $op1 * @param string $op2 * @return string */ public static function Mod($op1, $op2) { $op1 = self::exponent($op1); $op2 = self::exponent($op2); return bcmod($op1, $op2); } /** * BCComp - fixes a problem of BCMath and exponential numbers * * @param string $op1 * @param string $op2 * @param integer $scale * @return string */ public static function Comp($op1, $op2, $scale = null) { $op1 = self::exponent($op1, $scale); $op2 = self::exponent($op2, $scale); return bccomp($op1, $op2, $scale); } } if (!extension_loaded('bcmath') || (defined('TESTS_ZEND_LOCALE_BCMATH_ENABLED') && !TESTS_ZEND_LOCALE_BCMATH_ENABLED) ) { require_once 'Zend/Locale/Math/PhpMath.php'; Zend_Locale_Math_PhpMath::disable(); }