Math.php 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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-2008 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id$
  20. */
  21. /**
  22. * Utility class for proxying math function to bcmath functions, if present,
  23. * otherwise to PHP builtin math operators, with limited detection of overflow conditions.
  24. * Sampling of PHP environments and platforms suggests that at least 80% to 90% support bcmath.
  25. * Thus, this file should be as light as possible.
  26. *
  27. * @category Zend
  28. * @package Zend_Locale
  29. * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
  30. * @license http://framework.zend.com/license/new-bsd New BSD License
  31. */
  32. class Zend_Locale_Math
  33. {
  34. // support unit testing without using bcmath functions
  35. public static $_bcmathDisabled = false;
  36. public static $add = array('Zend_Locale_Math', 'Add');
  37. public static $sub = array('Zend_Locale_Math', 'Sub');
  38. public static $pow = array('Zend_Locale_Math', 'Pow');
  39. public static $mul = array('Zend_Locale_Math', 'Mul');
  40. public static $div = array('Zend_Locale_Math', 'Div');
  41. public static $comp = array('Zend_Locale_Math', 'Comp');
  42. public static $sqrt = array('Zend_Locale_Math', 'Sqrt');
  43. public static $mod = array('Zend_Locale_Math', 'Mod');
  44. public static $scale = 'bcscale';
  45. public static function isBcmathDisabled()
  46. {
  47. return self::$_bcmathDisabled;
  48. }
  49. /**
  50. * Surprisingly, the results of this implementation of round()
  51. * prove better than the native PHP round(). For example, try:
  52. * round(639.795, 2);
  53. * round(267.835, 2);
  54. * round(0.302515, 5);
  55. * round(0.36665, 4);
  56. * then try:
  57. * Zend_Locale_Math::round('639.795', 2);
  58. */
  59. public static function round($op1, $precision = 0)
  60. {
  61. if (self::$_bcmathDisabled) {
  62. return self::normalize(round($op1, $precision));
  63. }
  64. $op1 = trim(self::normalize($op1));
  65. $length = strlen($op1);
  66. if (($decPos = strpos($op1, '.')) === false) {
  67. $op1 .= '.0';
  68. $decPos = $length;
  69. $length += 2;
  70. }
  71. if ($precision < 0 && abs($precision) > $decPos) {
  72. return '0';
  73. }
  74. $digitsBeforeDot = $length - ($decPos + 1);
  75. if ($precision >= ($length - ($decPos + 1))) {
  76. return $op1;
  77. }
  78. if ($precision === 0) {
  79. $triggerPos = 1;
  80. $roundPos = -1;
  81. } elseif ($precision > 0) {
  82. $triggerPos = $precision + 1;
  83. $roundPos = $precision;
  84. } else {
  85. $triggerPos = $precision;
  86. $roundPos = $precision -1;
  87. }
  88. $triggerDigit = $op1[$triggerPos + $decPos];
  89. if ($precision < 0) {
  90. // zero fill digits to the left of the decimal place
  91. $op1 = substr($op1, 0, $decPos + $precision) . str_pad('', abs($precision), '0');
  92. }
  93. if ($triggerDigit >= '5') {
  94. if ($roundPos + $decPos == -1) {
  95. return str_pad('1', $decPos + 1, '0');
  96. }
  97. $roundUp = str_pad('', $length, '0');
  98. $roundUp[$decPos] = '.';
  99. $roundUp[$roundPos + $decPos] = '1';
  100. return bcadd($op1, $roundUp, $precision);
  101. } elseif ($precision >= 0) {
  102. return substr($op1, 0, $decPos + ($precision ? $precision + 1: 0));
  103. }
  104. return (string) $op1;
  105. }
  106. /**
  107. * Normalizes an input to standard english notation
  108. * Fixes a problem of BCMath with setLocale which is PHP related
  109. *
  110. * @param integer $value Value to normalize
  111. * @return string Normalized string without BCMath problems
  112. */
  113. public static function normalize($value)
  114. {
  115. $convert = localeconv();
  116. $value = str_replace($convert['thousands_sep'], "",(string) $value);
  117. $value = str_replace($convert['positive_sign'], "", $value);
  118. $value = str_replace($convert['decimal_point'], ".",$value);
  119. if (!empty($convert['negative_sign']) and (strpos($value, $convert['negative_sign']))) {
  120. $value = str_replace($convert['negative_sign'], "", $value);
  121. $value = "-" . $value;
  122. }
  123. return $value;
  124. }
  125. /**
  126. * Localizes an input from standard english notation
  127. * Fixes a problem of BCMath with setLocale which is PHP related
  128. *
  129. * @param integer $value Value to normalize
  130. * @return string Normalized string without BCMath problems
  131. */
  132. public static function localize($value)
  133. {
  134. $convert = localeconv();
  135. $value = str_replace(".", $convert['decimal_point'], (string) $value);
  136. if (!empty($convert['negative_sign']) and (strpos($value, "-"))) {
  137. $value = str_replace("-", $convert['negative_sign'], $value);
  138. }
  139. return $value;
  140. }
  141. /**
  142. * Changes exponential numbers to plain string numbers
  143. * Fixes a problem of BCMath with numbers containing exponents
  144. *
  145. * @param integer $value Value to erase the exponent
  146. * @param integer $scale (Optional) Scale to use
  147. * @return string
  148. */
  149. public static function exponent($value, $scale = null)
  150. {
  151. if (!extension_loaded('bcmath')) {
  152. return $value;
  153. }
  154. $split = explode('e', $value);
  155. if (count($split) == 1) {
  156. $split = explode('E', $value);
  157. }
  158. if (count($split) > 1) {
  159. $value = bcmul($split[0], bcpow(10, $split[1], $scale), $scale);
  160. }
  161. return $value;
  162. }
  163. /**
  164. * BCAdd - fixes a problem of BCMath and exponential numbers
  165. *
  166. * @param string $op1
  167. * @param string $op2
  168. * @param integer $scale
  169. * @return string
  170. */
  171. public static function Add($op1, $op2, $scale = null)
  172. {
  173. $op1 = self::exponent($op1, $scale);
  174. $op2 = self::exponent($op2, $scale);
  175. return bcadd($op1, $op2, $scale);
  176. }
  177. /**
  178. * BCSub - fixes a problem of BCMath and exponential numbers
  179. *
  180. * @param string $op1
  181. * @param string $op2
  182. * @param integer $scale
  183. * @return string
  184. */
  185. public static function Sub($op1, $op2, $scale = null)
  186. {
  187. $op1 = self::exponent($op1, $scale);
  188. $op2 = self::exponent($op2, $scale);
  189. return bcsub($op1, $op2, $scale);
  190. }
  191. /**
  192. * BCPow - fixes a problem of BCMath and exponential numbers
  193. *
  194. * @param string $op1
  195. * @param string $op2
  196. * @param integer $scale
  197. * @return string
  198. */
  199. public static function Pow($op1, $op2, $scale = null)
  200. {
  201. $op1 = self::exponent($op1, $scale);
  202. $op2 = self::exponent($op2, $scale);
  203. return bcpow($op1, $op2, $scale);
  204. }
  205. /**
  206. * BCMul - fixes a problem of BCMath and exponential numbers
  207. *
  208. * @param string $op1
  209. * @param string $op2
  210. * @param integer $scale
  211. * @return string
  212. */
  213. public static function Mul($op1, $op2, $scale = null)
  214. {
  215. $op1 = self::exponent($op1, $scale);
  216. $op2 = self::exponent($op2, $scale);
  217. return bcmul($op1, $op2, $scale);
  218. }
  219. /**
  220. * BCDiv - fixes a problem of BCMath and exponential numbers
  221. *
  222. * @param string $op1
  223. * @param string $op2
  224. * @param integer $scale
  225. * @return string
  226. */
  227. public static function Div($op1, $op2, $scale = null)
  228. {
  229. $op1 = self::exponent($op1, $scale);
  230. $op2 = self::exponent($op2, $scale);
  231. return bcdiv($op1, $op2, $scale);
  232. }
  233. /**
  234. * BCSqrt - fixes a problem of BCMath and exponential numbers
  235. *
  236. * @param string $op1
  237. * @param integer $scale
  238. * @return string
  239. */
  240. public static function Sqrt($op1, $scale = null)
  241. {
  242. $op1 = self::exponent($op1, $scale);
  243. return bcsqrt($op1, $scale);
  244. }
  245. /**
  246. * BCMod - fixes a problem of BCMath and exponential numbers
  247. *
  248. * @param string $op1
  249. * @param string $op2
  250. * @return string
  251. */
  252. public static function Mod($op1, $op2)
  253. {
  254. $op1 = self::exponent($op1);
  255. $op2 = self::exponent($op2);
  256. return bcmod($op1, $op2);
  257. }
  258. /**
  259. * BCComp - fixes a problem of BCMath and exponential numbers
  260. *
  261. * @param string $op1
  262. * @param string $op2
  263. * @param integer $scale
  264. * @return string
  265. */
  266. public static function Comp($op1, $op2, $scale = null)
  267. {
  268. $op1 = self::exponent($op1, $scale);
  269. $op2 = self::exponent($op2, $scale);
  270. return bccomp($op1, $op2, $scale);
  271. }
  272. }
  273. if ((defined('TESTS_ZEND_LOCALE_BCMATH_ENABLED') && !TESTS_ZEND_LOCALE_BCMATH_ENABLED)
  274. || !extension_loaded('bcmath')) {
  275. require_once 'Zend/Locale/Math/PhpMath.php';
  276. Zend_Locale_Math_PhpMath::disable();
  277. }