source: trunk/gcc/libjava/java/math/BigDecimal.java

Last change on this file was 1392, checked in by bird, 21 years ago

This commit was generated by cvs2svn to compensate for changes in r1391,
which included commits to RCS files with non-trunk default branches.

  • Property cvs2svn:cvs-rev set to 1.1.1.2
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 13.7 KB
Line 
1/* java.math.BigDecimal -- Arbitrary precision decimals.
2 Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
3
4This file is part of GNU Classpath.
5
6GNU Classpath is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Classpath is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Classpath; see the file COPYING. If not, write to the
18Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
1902111-1307 USA.
20
21Linking this library statically or dynamically with other modules is
22making a combined work based on this library. Thus, the terms and
23conditions of the GNU General Public License cover the whole
24combination.
25
26As a special exception, the copyright holders of this library give you
27permission to link this library with independent modules to produce an
28executable, regardless of the license terms of these independent
29modules, and to copy and distribute the resulting executable under
30terms of your choice, provided that you also meet, for each linked
31independent module, the terms and conditions of the license of that
32module. An independent module is a module which is not derived from
33or based on this library. If you modify this library, you may extend
34this exception to your version of the library, but you are not
35obligated to do so. If you do not wish to do so, delete this
36exception statement from your version. */
37
38package java.math;
39
40import java.math.BigInteger;
41
42public class BigDecimal extends Number implements Comparable
43{
44 private BigInteger intVal;
45 private int scale;
46 private static final long serialVersionUID = 6108874887143696463L;
47
48 private final static BigDecimal ZERO =
49 new BigDecimal (BigInteger.valueOf (0), 0);
50
51 private final static BigDecimal ONE =
52 new BigDecimal (BigInteger.valueOf (1), 0);
53
54 public final static int ROUND_UP = 0;
55 public final static int ROUND_DOWN = 1;
56 public final static int ROUND_CEILING = 2;
57 public final static int ROUND_FLOOR = 3;
58 public final static int ROUND_HALF_UP = 4;
59 public final static int ROUND_HALF_DOWN = 5;
60 public final static int ROUND_HALF_EVEN = 6;
61 public final static int ROUND_UNNECESSARY = 7;
62
63 public BigDecimal (BigInteger num)
64 {
65 this (num, 0);
66 }
67
68 public BigDecimal (BigInteger num, int scale) throws NumberFormatException
69 {
70 if (scale < 0)
71 throw new NumberFormatException ("scale of " + scale + " is < 0");
72 this.intVal = num;
73 this.scale = scale;
74 }
75
76 public BigDecimal (double num) throws NumberFormatException
77 {
78 if (Double.isInfinite (num) || Double.isNaN (num))
79 throw new NumberFormatException ("invalid argument: " + num);
80 // Note we can't convert NUM to a String and then use the
81 // String-based constructor. The BigDecimal documentation makes
82 // it clear that the two constructors work differently.
83
84 final int mantissaBits = 52;
85 final int exponentBits = 11;
86 final long mantMask = (1L << mantissaBits) - 1;
87 final long expMask = (1L << exponentBits) - 1;
88
89 long bits = Double.doubleToLongBits (num);
90 long mantissa = bits & mantMask;
91 long exponent = (bits >>> mantissaBits) & expMask;
92 boolean denormal = exponent == 0;
93 // Correct the exponent for the bias.
94 exponent -= denormal ? 1022 : 1023;
95 // Now correct the exponent to account for the bits to the right
96 // of the decimal.
97 exponent -= mantissaBits;
98 // Ordinary numbers have an implied leading `1' bit.
99 if (! denormal)
100 mantissa |= (1L << mantissaBits);
101
102 // Shave off factors of 10.
103 while (exponent < 0 && (mantissa & 1) == 0)
104 {
105 ++exponent;
106 mantissa >>= 1;
107 }
108
109 intVal = BigInteger.valueOf (bits < 0 ? - mantissa : mantissa);
110 if (exponent < 0)
111 {
112 // We have MANTISSA * 2 ^ (EXPONENT).
113 // Since (1/2)^N == 5^N * 10^-N we can easily convert this
114 // into a power of 10.
115 scale = (int) (- exponent);
116 BigInteger mult = BigInteger.valueOf (5).pow (scale);
117 intVal = intVal.multiply (mult);
118 }
119 else
120 {
121 intVal = intVal.shiftLeft ((int) exponent);
122 scale = 0;
123 }
124 }
125
126 public BigDecimal (String num) throws NumberFormatException
127 {
128 int len = num.length();
129 int start = 0, point = 0;
130 int dot = -1;
131 boolean negative = false;
132 if (num.charAt(0) == '+')
133 {
134 ++start;
135 ++point;
136 }
137 else if (num.charAt(0) == '-')
138 {
139 ++start;
140 ++point;
141 negative = true;
142 }
143
144 while (point < len)
145 {
146 char c = num.charAt (point);
147 if (c == '.')
148 {
149 if (dot >= 0)
150 throw new NumberFormatException ("multiple `.'s in number");
151 dot = point;
152 }
153 else if (c == 'e' || c == 'E')
154 break;
155 else if (Character.digit (c, 10) < 0)
156 throw new NumberFormatException ("unrecognized character: " + c);
157 ++point;
158 }
159
160 String val;
161 if (dot >= 0)
162 {
163 val = num.substring (start, dot) + num.substring (dot + 1, point);
164 scale = point - 1 - dot;
165 }
166 else
167 {
168 val = num.substring (start, point);
169 scale = 0;
170 }
171 if (val.length () == 0)
172 throw new NumberFormatException ("no digits seen");
173
174 if (negative)
175 val = "-" + val;
176 intVal = new BigInteger (val);
177
178 // Now parse exponent.
179 if (point < len)
180 {
181 point++;
182 if (num.charAt(point) == '+')
183 point++;
184
185 if (point >= len )
186 throw new NumberFormatException ("no exponent following e or E");
187
188 try
189 {
190 int exp = Integer.parseInt (num.substring (point));
191 exp -= scale;
192 if (signum () == 0)
193 scale = 0;
194 else if (exp > 0)
195 {
196 intVal = intVal.multiply (BigInteger.valueOf (10).pow (exp));
197 scale = 0;
198 }
199 else
200 scale = - exp;
201 }
202 catch (NumberFormatException ex)
203 {
204 throw new NumberFormatException ("malformed exponent");
205 }
206 }
207 }
208
209 public static BigDecimal valueOf (long val)
210 {
211 return valueOf (val, 0);
212 }
213
214 public static BigDecimal valueOf (long val, int scale)
215 throws NumberFormatException
216 {
217 if ((scale == 0) && ((int)val == val))
218 switch ((int) val)
219 {
220 case 0:
221 return ZERO;
222 case 1:
223 return ONE;
224 }
225
226 return new BigDecimal (BigInteger.valueOf (val), scale);
227 }
228
229 public BigDecimal add (BigDecimal val)
230 {
231 // For addition, need to line up decimals. Note that the movePointRight
232 // method cannot be used for this as it might return a BigDecimal with
233 // scale == 0 instead of the scale we need.
234 BigInteger op1 = intVal;
235 BigInteger op2 = val.intVal;
236 if (scale < val.scale)
237 op1 = op1.multiply (BigInteger.valueOf (10).pow (val.scale - scale));
238 else if (scale > val.scale)
239 op2 = op2.multiply (BigInteger.valueOf (10).pow (scale - val.scale));
240
241 return new BigDecimal (op1.add (op2), Math.max (scale, val.scale));
242 }
243
244 public BigDecimal subtract (BigDecimal val)
245 {
246 return this.add(val.negate());
247 }
248
249 public BigDecimal multiply (BigDecimal val)
250 {
251 return new BigDecimal (intVal.multiply (val.intVal), scale + val.scale);
252 }
253
254 public BigDecimal divide (BigDecimal val, int roundingMode)
255 throws ArithmeticException, IllegalArgumentException
256 {
257 return divide (val, scale, roundingMode);
258 }
259
260 public BigDecimal divide(BigDecimal val, int newScale, int roundingMode)
261 throws ArithmeticException, IllegalArgumentException
262 {
263 if (roundingMode < 0 || roundingMode > 7)
264 throw
265 new IllegalArgumentException("illegal rounding mode: " + roundingMode);
266
267 if (newScale < 0)
268 throw new ArithmeticException ("scale is negative: " + newScale);
269
270 if (intVal.signum () == 0) // handle special case of 0.0/0.0
271 return newScale == 0 ? ZERO : new BigDecimal (ZERO.intVal, newScale);
272
273 // Ensure that pow gets a non-negative value.
274 int valScale = val.scale;
275 BigInteger valIntVal = val.intVal;
276 int power = newScale + 1 - (scale - val.scale);
277 if (power < 0)
278 {
279 // Effectively increase the scale of val to avoid an
280 // ArithmeticException for a negative power.
281 valIntVal = valIntVal.multiply (BigInteger.valueOf (10).pow (-power));
282 power = 0;
283 }
284
285 BigInteger dividend = intVal.multiply (BigInteger.valueOf (10).pow (power));
286
287 BigInteger parts[] = dividend.divideAndRemainder (valIntVal);
288// System.out.println("int: " + parts[0]);
289// System.out.println("rem: " + parts[1]);
290
291 int roundDigit = parts[0].mod (BigInteger.valueOf (10)).intValue ();
292 BigInteger unrounded = parts[0].divide (BigInteger.valueOf (10));
293
294 if (roundDigit == 0 && parts[1].signum () == 0) // no rounding necessary
295 return new BigDecimal (unrounded, newScale);
296
297 int sign = unrounded.signum ();
298
299 switch (roundingMode)
300 {
301 case ROUND_UNNECESSARY:
302 throw new ArithmeticException ("newScale is not large enough");
303 case ROUND_CEILING:
304 roundingMode = (sign == 1) ? ROUND_UP : ROUND_DOWN;
305 break;
306 case ROUND_FLOOR:
307 roundingMode = (sign == 1) ? ROUND_DOWN : ROUND_UP;
308 break;
309 case ROUND_HALF_UP:
310 roundingMode = (roundDigit >= 5) ? ROUND_UP : ROUND_DOWN;
311 break;
312 case ROUND_HALF_DOWN:
313 roundingMode = (roundDigit > 5) ? ROUND_UP : ROUND_DOWN;
314 break;
315 case ROUND_HALF_EVEN:
316 if (roundDigit < 5)
317 roundingMode = ROUND_DOWN;
318 else
319 {
320 int rightmost =
321 unrounded.mod (BigInteger.valueOf (10)).intValue ();
322 if (rightmost % 2 == 1) // odd, then ROUND_HALF_UP
323 roundingMode = ROUND_UP;
324 else // even, then ROUND_HALF_DOWN
325 roundingMode = (roundDigit > 5) ? ROUND_UP : ROUND_DOWN;
326 }
327 break;
328 }
329
330 if (roundingMode == ROUND_UP)
331 return new BigDecimal (unrounded.add (BigInteger.valueOf (1)), newScale);
332
333 // roundingMode == ROUND_DOWN
334 return new BigDecimal (unrounded, newScale);
335 }
336
337 public int compareTo (BigDecimal val)
338 {
339 if (scale == val.scale)
340 return intVal.compareTo (val.intVal);
341
342 BigInteger thisParts[] =
343 intVal.divideAndRemainder (BigInteger.valueOf (10).pow (scale));
344 BigInteger valParts[] =
345 val.intVal.divideAndRemainder (BigInteger.valueOf (10).pow (val.scale));
346
347 int compare;
348 if ((compare = thisParts[0].compareTo (valParts[0])) != 0)
349 return compare;
350
351 // quotients are the same, so compare remainders
352
353 // remove trailing zeros
354 if (thisParts[1].equals (BigInteger.valueOf (0)) == false)
355 while (thisParts[1].mod (BigInteger.valueOf (10)).equals
356 (BigInteger.valueOf (0)))
357 thisParts[1] = thisParts[1].divide (BigInteger.valueOf (10));
358 // again...
359 if (valParts[1].equals(BigInteger.valueOf (0)) == false)
360 while (valParts[1].mod (BigInteger.valueOf (10)).equals
361 (BigInteger.valueOf (0)))
362 valParts[1] = valParts[1].divide (BigInteger.valueOf (10));
363
364 // and compare them
365 return thisParts[1].compareTo (valParts[1]);
366 }
367
368 public int compareTo (Object val)
369 {
370 return(compareTo((BigDecimal)val));
371 }
372
373 public boolean equals (Object o)
374 {
375 return (o instanceof BigDecimal
376 && scale == ((BigDecimal) o).scale
377 && compareTo ((BigDecimal) o) == 0);
378 }
379
380 public int hashCode()
381 {
382 return intValue() ^ scale;
383 }
384
385 public BigDecimal max (BigDecimal val)
386 {
387 switch (compareTo (val))
388 {
389 case 1:
390 return this;
391 default:
392 return val;
393 }
394 }
395
396 public BigDecimal min (BigDecimal val)
397 {
398 switch (compareTo (val))
399 {
400 case -1:
401 return this;
402 default:
403 return val;
404 }
405 }
406
407 public BigDecimal movePointLeft (int n)
408 {
409 return (n < 0) ? movePointRight (-n) : new BigDecimal (intVal, scale + n);
410 }
411
412 public BigDecimal movePointRight (int n)
413 {
414 if (n < 0)
415 return movePointLeft (-n);
416
417 if (scale >= n)
418 return new BigDecimal (intVal, scale - n);
419
420 return new BigDecimal (intVal.multiply
421 (BigInteger.valueOf (10).pow (n - scale)), 0);
422 }
423
424 public int signum ()
425 {
426 return intVal.signum ();
427 }
428
429 public int scale ()
430 {
431 return scale;
432 }
433
434 public BigInteger unscaledValue()
435 {
436 return intVal;
437 }
438
439 public BigDecimal abs ()
440 {
441 return new BigDecimal (intVal.abs (), scale);
442 }
443
444 public BigDecimal negate ()
445 {
446 return new BigDecimal (intVal.negate (), scale);
447 }
448
449 public String toString ()
450 {
451 String bigStr = intVal.toString();
452 if (scale == 0)
453 return bigStr;
454
455 boolean negative = (bigStr.charAt(0) == '-');
456
457 int point = bigStr.length() - scale - (negative ? 1 : 0);
458
459 StringBuffer sb = new StringBuffer(bigStr.length() + 2 +
460 (point <= 0 ? (-point + 1) : 0));
461 if (point <= 0)
462 {
463 if (negative)
464 sb.append('-');
465 sb.append('0').append('.');
466 while (point < 0)
467 {
468 sb.append('0');
469 point++;
470 }
471 sb.append(bigStr.substring(negative ? 1 : 0));
472 }
473 else
474 {
475 sb.append(bigStr);
476 sb.insert(point + (negative ? 1 : 0), '.');
477 }
478 return sb.toString();
479 }
480
481 public BigInteger toBigInteger ()
482 {
483 return scale == 0 ? intVal :
484 intVal.divide (BigInteger.valueOf (10).pow (scale));
485 }
486
487 public int intValue ()
488 {
489 return toBigInteger ().intValue ();
490 }
491
492 public long longValue ()
493 {
494 return toBigInteger().longValue();
495 }
496
497 public float floatValue()
498 {
499 return Float.valueOf(toString()).floatValue();
500 }
501
502 public double doubleValue()
503 {
504 return Double.valueOf(toString()).doubleValue();
505 }
506
507 public BigDecimal setScale (int scale) throws ArithmeticException
508 {
509 return setScale (scale, ROUND_UNNECESSARY);
510 }
511
512 public BigDecimal setScale (int scale, int roundingMode)
513 throws ArithmeticException, IllegalArgumentException
514 {
515 return divide (ONE, scale, roundingMode);
516 }
517}
Note: See TracBrowser for help on using the repository browser.