1 29 30 package com.caucho.quercus.lib; 31 32 import com.caucho.quercus.annotation.Optional; 33 import com.caucho.quercus.env.DoubleValue; 34 import com.caucho.quercus.env.Env; 35 import com.caucho.quercus.env.LongValue; 36 import com.caucho.quercus.env.StringValue; 37 import com.caucho.quercus.env.StringValueImpl; 38 import com.caucho.quercus.env.Value; 39 import com.caucho.quercus.module.AbstractQuercusModule; 40 import com.caucho.util.L10N; 41 42 import java.math.BigDecimal ; 43 import java.math.BigInteger ; 44 import java.math.MathContext ; 45 import java.math.RoundingMode ; 46 import java.util.HashMap ; 47 import java.util.Map ; 48 49 52 public class BcmathModule extends AbstractQuercusModule { 53 private static final L10N L = new L10N(BcmathModule.class); 54 55 private static final BigDecimal ZERO = BigDecimal.ZERO; 56 private static final BigDecimal ONE = BigDecimal.ONE; 57 private static final BigDecimal TWO = new BigDecimal (2); 58 private static final int SQRT_MAX_ITERATIONS = 50; 59 60 private static final HashMap <String ,StringValue> _iniMap 61 = new HashMap <String ,StringValue>(); 62 63 static { 64 addIni(_iniMap, "bcmath.scale", "0", PHP_INI_ALL); 65 } 66 67 public String []getLoadedExtensions() 68 { 69 return new String [] { "bcmath" }; 70 } 71 72 public Map <String ,StringValue> getDefaultIni() 73 { 74 return _iniMap; 75 } 76 77 private static BigDecimal toBigDecimal(Value value) 78 { 79 try { 80 if (value instanceof StringValue) 81 return new BigDecimal (value.toString()); 82 if (value instanceof DoubleValue) 83 return new BigDecimal (value.toDouble()); 84 else if (value instanceof LongValue) 85 return new BigDecimal (value.toLong()); 86 else 87 return new BigDecimal (value.toString()); 88 } 89 catch (NumberFormatException ex) { 90 return ZERO; 91 } 92 catch (IllegalArgumentException ex) { 93 return ZERO; 94 } 95 } 96 97 private static int calculateScale(Env env, int scale) 98 { 99 if (scale < 0) { 100 Value iniValue = env.getIni("bcmath.scale"); 101 102 if (iniValue != null) 103 scale = iniValue.toInt(); 104 } 105 106 if (scale < 0) 107 scale = 0; 108 109 return scale; 110 } 111 112 119 public static String bcadd(Env env, Value value1, Value value2, @Optional("-1") int scale) 120 { 121 scale = calculateScale(env, scale); 122 123 BigDecimal bd1 = toBigDecimal(value1); 124 BigDecimal bd2 = toBigDecimal(value2); 125 126 BigDecimal bd = bd1.add(bd2); 127 128 bd = bd.setScale(scale, RoundingMode.DOWN); 129 130 return bd.toPlainString(); 131 } 132 133 141 public static int bccomp(Env env, Value value1, Value value2, @Optional("-1") int scale) 142 { 143 scale = calculateScale(env, scale); 144 145 BigDecimal bd1 = toBigDecimal(value1); 146 BigDecimal bd2 = toBigDecimal(value2); 147 148 bd1 = bd1.setScale(scale, RoundingMode.DOWN); 149 bd2 = bd2.setScale(scale, RoundingMode.DOWN); 150 151 return bd1.compareTo(bd2); 152 } 153 154 163 public static String bcdiv(Env env, Value value1, Value value2, @Optional("-1") int scale) 164 { 165 scale = calculateScale(env, scale); 166 167 BigDecimal bd1 = toBigDecimal(value1); 168 BigDecimal bd2 = toBigDecimal(value2); 169 170 if (bd2.compareTo(ZERO) == 0) { 171 env.warning(L.l("division by zero")); 172 return null; 173 } 174 175 BigDecimal result; 176 177 if (scale > 0) { 178 result = bd1.divide(bd2, scale + 2, RoundingMode.DOWN); 179 } 180 else { 181 result = bd1.divide(bd2, 2, RoundingMode.DOWN); 182 } 183 184 result = result.setScale(scale, RoundingMode.DOWN); 185 186 return result.toPlainString(); 187 } 188 189 196 public static String bcmod(Env env, Value value, Value modulus) 197 { 198 BigDecimal bd1 = toBigDecimal(value).setScale(0, RoundingMode.DOWN); 199 BigDecimal bd2 = toBigDecimal(modulus).setScale(0, RoundingMode.DOWN); 200 201 if (bd2.compareTo(ZERO) == 0) { 202 env.warning(L.l("division by zero")); 203 return null; 204 } 205 206 BigDecimal bd = bd1.remainder(bd2, MathContext.DECIMAL128); 207 208 bd = bd.setScale(0, RoundingMode.DOWN); 210 211 return bd.toPlainString(); 212 } 213 214 221 public static String bcmul(Env env, Value value1, Value value2, @Optional("-1") int scale) 222 { 223 scale = calculateScale(env, scale); 224 225 BigDecimal bd1 = toBigDecimal(value1); 226 BigDecimal bd2 = toBigDecimal(value2); 227 228 BigDecimal bd = bd1.multiply(bd2); 229 230 if (bd.compareTo(ZERO) == 0) { 232 if (scale > 0) 233 return "0.0"; 234 else 235 return "0"; 236 } 237 238 bd = bd.setScale(scale, RoundingMode.DOWN); 239 bd = bd.stripTrailingZeros(); 240 241 return bd.toPlainString(); 242 } 243 244 253 public static String bcpow(Env env, Value base, Value exp, @Optional("-1") int scale) 254 { 255 scale = calculateScale(env, scale); 256 257 BigDecimal bd1 = toBigDecimal(base); 258 BigDecimal bd2 = toBigDecimal(exp); 259 260 if (bd2.scale() > 0) 261 env.warning("fractional exponent not supported"); 262 263 int exponent = bd2.toBigInteger().intValue(); 264 265 if (exponent == 0) 266 return "1"; 267 268 boolean isNeg; 269 270 if (exponent < 0) { 271 isNeg = true; 272 exponent *= -1; 273 } 274 else 275 isNeg = false; 276 277 BigDecimal bd = bd1.pow(exponent); 278 279 if (isNeg) 280 bd = ONE.divide(bd, scale + 2, RoundingMode.DOWN); 281 282 bd = bd.setScale(scale, RoundingMode.DOWN); 283 284 if (bd.compareTo(BigDecimal.ZERO) == 0) 285 return "0"; 286 287 bd = bd.stripTrailingZeros(); 288 289 return bd.toPlainString(); 290 } 291 292 303 public static String bcpowmod(Env env, Value base, Value exp, Value modulus, @Optional("-1") int scale) 304 { 305 scale = calculateScale(env, scale); 306 307 String pow = bcpow(env, base, exp, scale); 309 310 if (pow == null) 311 return null; 312 313 return bcmod(env, new StringValueImpl(pow), modulus); 314 } 315 316 317 325 public static boolean bcscale(Env env, int scale) 326 { 327 env.setIni("bcmath.scale", String.valueOf(scale)); 328 329 return true; 330 } 331 332 341 public static String bcsqrt(Env env, Value operand, @Optional("-1") int scale) 342 { 343 scale = calculateScale(env, scale); 344 345 BigDecimal value = toBigDecimal(operand); 346 347 int compareToZero = value.compareTo(ZERO); 348 349 if (compareToZero < 0) { 350 env.warning(L.l("square root of negative number")); 351 return null; 352 } 353 else if (compareToZero == 0) { 354 return "0"; 355 } 356 357 int compareToOne = value.compareTo(ONE); 358 359 if (compareToOne == 0) 360 return "1"; 361 362 364 int cscale; 365 366 368 BigDecimal initialGuess; 369 370 if (compareToOne < 1) { 371 initialGuess = ONE; 372 cscale = value.scale(); 373 } 374 else { 375 BigInteger integerPart = value.toBigInteger(); 376 377 int length = integerPart.toString().length(); 378 379 if ((length % 2) == 0) 380 length--; 381 382 length /= 2; 383 384 initialGuess = ONE.movePointRight(length); 385 386 cscale = Math.max(scale, value.scale()) + 2; 387 } 388 389 391 BigDecimal guess = initialGuess; 392 393 BigDecimal lastGuess; 394 395 for (int iteration = 0; iteration < SQRT_MAX_ITERATIONS; iteration++) { 396 lastGuess = guess; 397 guess = value.divide(guess, cscale, RoundingMode.DOWN); 398 guess = guess.add(lastGuess); 399 guess = guess.divide(TWO, cscale, RoundingMode.DOWN); 400 401 if (lastGuess.equals(guess)) { 402 break; 403 } 404 } 405 406 value = guess; 407 408 value = value.setScale(scale, RoundingMode.DOWN); 409 410 return value.toPlainString(); 411 } 412 413 420 public static String bcsub(Env env, Value value1, Value value2, @Optional("-1") int scale) 421 { 422 scale = calculateScale(env, scale); 423 424 BigDecimal bd1 = toBigDecimal(value1); 425 BigDecimal bd2 = toBigDecimal(value2); 426 427 BigDecimal bd = bd1.subtract(bd2); 428 429 bd = bd.setScale(scale, RoundingMode.DOWN); 430 431 return bd.toPlainString(); 432 } 433 } 434 | Popular Tags |