KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > util > BigNumber


1 /**
2  * com.mckoi.util.BigNumber 26 Jul 2002
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.util;
26
27 import java.math.BigDecimal JavaDoc;
28 import java.math.BigInteger JavaDoc;
29
30 /**
31  * Extends BigDecimal to allow a number to be positive infinity, negative
32  * infinity and not-a-number. This provides compatibility with float and
33  * double types.
34  *
35  * @author Tobias Downer
36  */

37
38 public final class BigNumber extends Number JavaDoc {
39
40   static final long serialVersionUID = -8681578742639638105L;
41
42   /**
43    * State enumerations.
44    */

45   private final static byte NEG_INF_STATE = 1;
46   private final static byte POS_INF_STATE = 2;
47   private final static byte NaN_STATE = 3;
48
49   /**
50    * The state of the number, either 0 for number is the BigDecimal, 1 for
51    * negative infinity, 2 for positive infinity and 3 for NaN.
52    */

53   private byte number_state;
54   
55   /**
56    * The BigDecimal representation.
57    */

58   private BigDecimal JavaDoc big_decimal;
59   
60   /**
61    * A 'long' representation of this number.
62    */

63   private long long_representation;
64
65   /**
66    * If this can be represented as an int or long, this contains the number
67    * of bytes needed to represent the number.
68    */

69   private byte byte_count = 120;
70   
71   /**
72    * Constructs the number.
73    */

74   private BigNumber(byte number_state, BigDecimal JavaDoc big_decimal) {
75     this.number_state = number_state;
76     if (number_state == 0) {
77       setBigDecimal(big_decimal);
78     }
79   }
80
81   private BigNumber(byte[] buf, int scale, byte state) {
82     this.number_state = state;
83     if (number_state == 0) {
84       BigInteger JavaDoc bigint = new BigInteger JavaDoc(buf);
85       setBigDecimal(new BigDecimal JavaDoc(bigint, scale));
86     }
87   }
88
89   // Only call this from a constructor!
90
private void setBigDecimal(BigDecimal JavaDoc big_decimal) {
91     this.big_decimal = big_decimal;
92     if (big_decimal.scale() == 0) {
93       BigInteger JavaDoc bint = big_decimal.toBigInteger();
94       int bit_count = big_decimal.toBigInteger().bitLength();
95       if (bit_count < 30) {
96         this.long_representation = bint.longValue();
97         this.byte_count = 4;
98       }
99       else if (bit_count < 60) {
100         this.long_representation = bint.longValue();
101         this.byte_count = 8;;
102       }
103     }
104   }
105   
106   /**
107    * Returns true if this BigNumber can be represented by a 64-bit long (has
108    * no scale).
109    */

110   public boolean canBeRepresentedAsLong() {
111     return byte_count <= 8;
112   }
113
114   /**
115    * Returns true if this BigNumber can be represented by a 32-bit int (has
116    * no scale).
117    */

118   public boolean canBeRepresentedAsInt() {
119     return byte_count <= 4;
120   }
121   
122   /**
123    * Returns the scale of this number, or -1 if the number has no scale (if
124    * it -inf, +inf or NaN).
125    */

126   public int getScale() {
127     if (number_state == 0) {
128       return big_decimal.scale();
129     }
130     else {
131       return -1;
132     }
133   }
134   
135   /**
136    * Returns the state of this number. Returns either 1 which indicates
137    * negative infinity, 2 which indicates positive infinity, or 3 which
138    * indicates NaN.
139    */

140   public byte getState() {
141     return number_state;
142   }
143   
144   /**
145    * Returns the inverse of the state.
146    */

147   private byte getInverseState() {
148     if (number_state == NEG_INF_STATE) {
149       return POS_INF_STATE;
150     }
151     else if (number_state == POS_INF_STATE) {
152       return NEG_INF_STATE;
153     }
154     else {
155       return number_state;
156     }
157   }
158   
159   /**
160    * Returns this number as a byte array (unscaled).
161    */

162   public byte[] toByteArray() {
163     if (number_state == 0) {
164       return big_decimal.movePointRight(
165                              big_decimal.scale()).toBigInteger().toByteArray();
166 // [ NOTE: The following code is 1.2+ only but BigNumber should be compatible
167
// with 1.1 so we use the above call ]
168
// return big_decimal.unscaledValue().toByteArray();
169
}
170     else {
171       return new byte[0];
172     }
173   }
174
175   /**
176    * Returns this big number as a string.
177    */

178   public String JavaDoc toString() {
179     switch (number_state) {
180       case(0):
181         return big_decimal.toString();
182       case(NEG_INF_STATE):
183         return "-Infinity";
184       case(POS_INF_STATE):
185         return "Infinity";
186       case(NaN_STATE):
187         return "NaN";
188       default:
189         throw new Error JavaDoc("Unknown number state");
190     }
191   }
192
193   /**
194    * Returns this big number as a double.
195    */

196   public double doubleValue() {
197     switch (number_state) {
198       case(0):
199         return big_decimal.doubleValue();
200       case(NEG_INF_STATE):
201         return Double.NEGATIVE_INFINITY;
202       case(POS_INF_STATE):
203         return Double.POSITIVE_INFINITY;
204       case(NaN_STATE):
205         return Double.NaN;
206       default:
207         throw new Error JavaDoc("Unknown number state");
208     }
209   }
210
211   /**
212    * Returns this big number as a float.
213    */

214   public float floatValue() {
215     switch (number_state) {
216       case(0):
217         return big_decimal.floatValue();
218       case(NEG_INF_STATE):
219         return Float.NEGATIVE_INFINITY;
220       case(POS_INF_STATE):
221         return Float.POSITIVE_INFINITY;
222       case(NaN_STATE):
223         return Float.NaN;
224       default:
225         throw new Error JavaDoc("Unknown number state");
226     }
227   }
228
229   /**
230    * Returns this big number as a long.
231    */

232   public long longValue() {
233     if (canBeRepresentedAsLong()) {
234       return long_representation;
235     }
236     switch (number_state) {
237       case(0):
238         return big_decimal.longValue();
239       default:
240         return (long) doubleValue();
241     }
242   }
243
244   /**
245    * Returns this big number as an int.
246    */

247   public int intValue() {
248     if (canBeRepresentedAsLong()) {
249       return (int) long_representation;
250     }
251     switch (number_state) {
252       case(0):
253         return big_decimal.intValue();
254       default:
255         return (int) doubleValue();
256     }
257   }
258
259   /**
260    * Returns this big number as a short.
261    */

262   public short shortValue() {
263     return (short) intValue();
264   }
265
266   /**
267    * Returns this big number as a byte.
268    */

269   public byte byteValue() {
270     return (byte) intValue();
271   }
272
273
274   /**
275    * Returns the big number as a BigDecimal object. Note that this throws
276    * an arith error if this number represents NaN, +Inf or -Inf.
277    */

278   public BigDecimal JavaDoc asBigDecimal() {
279     if (number_state == 0) {
280       return big_decimal;
281     }
282     else {
283       throw new ArithmeticException JavaDoc(
284           "NaN, +Infinity or -Infinity can't be translated to a BigDecimal");
285     }
286   }
287
288   /**
289    * Compares this BigNumber with the given BigNumber. Returns 0 if the values
290    * are equal, >0 if this is greater than the given value, and &lt; 0 if this
291    * is less than the given value.
292    */

293   public int compareTo(BigNumber number) {
294     
295     if (this == number) {
296       return 0;
297     }
298     
299     // If this is a non-infinity number
300
if (number_state == 0) {
301       
302       // If both values can be represented by a long value
303
if (canBeRepresentedAsLong() && number.canBeRepresentedAsLong()) {
304         // Perform a long comparison check,
305
if (long_representation > number.long_representation) {
306           return 1;
307         }
308         else if (long_representation < number.long_representation) {
309           return -1;
310         }
311         else {
312           return 0;
313         }
314     
315       }
316       
317       // And the compared number is non-infinity then use the BigDecimal
318
// compareTo method.
319
if (number.number_state == 0) {
320         return big_decimal.compareTo(number.big_decimal);
321       }
322       else {
323         // Comparing a regular number with a NaN number.
324
// If positive infinity or if NaN
325
if (number.number_state == POS_INF_STATE ||
326             number.number_state == NaN_STATE) {
327           return -1;
328         }
329         // If negative infinity
330
else if (number.number_state == NEG_INF_STATE) {
331           return 1;
332         }
333         else {
334           throw new Error JavaDoc("Unknown number state.");
335         }
336       }
337     }
338     else {
339       // This number is a NaN number.
340
// Are we comparing with a regular number?
341
if (number.number_state == 0) {
342         // Yes, negative infinity
343
if (number_state == NEG_INF_STATE) {
344           return -1;
345         }
346         // positive infinity or NaN
347
else if (number_state == POS_INF_STATE ||
348                  number_state == NaN_STATE) {
349           return 1;
350         }
351         else {
352           throw new Error JavaDoc("Unknown number state.");
353         }
354       }
355       else {
356         // Comparing NaN number with a NaN number.
357
// This compares -Inf less than Inf and NaN and NaN greater than
358
// Inf and -Inf. -Inf < Inf < NaN
359
return (int) (number_state - number.number_state);
360       }
361     }
362   }
363   
364   /**
365    * The equals comparison uses the BigDecimal 'equals' method to compare
366    * values. This means that '0' is NOT equal to '0.0' and '10.0' is NOT equal
367    * to '10.00'. Care should be taken when using this method.
368    */

369   public boolean equals(Object JavaDoc ob) {
370     BigNumber bnum = (BigNumber) ob;
371     if (number_state != 0) {
372       return (number_state == bnum.number_state);
373     }
374     else {
375       return big_decimal.equals(bnum.big_decimal);
376     }
377   }
378
379
380
381
382   /**
383    * Statics.
384    */

385   private final static BigDecimal JavaDoc BD_ZERO = new BigDecimal JavaDoc(0);
386
387
388   
389   // ---- Mathematical functions ----
390

391   public BigNumber bitWiseOr(BigNumber number) {
392     if (number_state == 0 && getScale() == 0 &&
393         number.number_state == 0 && number.getScale() == 0) {
394       BigInteger JavaDoc bi1 = big_decimal.toBigInteger();
395       BigInteger JavaDoc bi2 = number.big_decimal.toBigInteger();
396       return new BigNumber((byte) 0, new BigDecimal JavaDoc(bi1.or(bi2)));
397     }
398     else {
399       return null;
400     }
401   }
402
403   public BigNumber add(BigNumber number) {
404     if (number_state == 0) {
405       if (number.number_state == 0) {
406         return new BigNumber((byte) 0, big_decimal.add(number.big_decimal));
407       }
408       else {
409         return new BigNumber(number.number_state, null);
410       }
411     }
412     else {
413       return new BigNumber(number_state, null);
414     }
415   }
416
417   public BigNumber subtract(BigNumber number) {
418     if (number_state == 0) {
419       if (number.number_state == 0) {
420         return new BigNumber((byte) 0, big_decimal.subtract(number.big_decimal));
421       }
422       else {
423         return new BigNumber(number.getInverseState(), null);
424       }
425     }
426     else {
427       return new BigNumber(number_state, null);
428     }
429   }
430   
431   public BigNumber multiply(BigNumber number) {
432     if (number_state == 0) {
433       if (number.number_state == 0) {
434         return new BigNumber((byte) 0, big_decimal.multiply(number.big_decimal));
435       }
436       else {
437         return new BigNumber(number.number_state, null);
438       }
439     }
440     else {
441       return new BigNumber(number_state, null);
442     }
443   }
444
445   public BigNumber divide(BigNumber number) {
446     if (number_state == 0) {
447       if (number.number_state == 0) {
448         BigDecimal JavaDoc div_by = number.big_decimal;
449         if (div_by.compareTo(BD_ZERO) != 0) {
450           return new BigNumber((byte) 0,
451                      big_decimal.divide(div_by, 10, BigDecimal.ROUND_HALF_UP));
452         }
453       }
454     }
455     // Return NaN if we can't divide
456
return new BigNumber((byte) 3, null);
457   }
458
459   public BigNumber abs() {
460     if (number_state == 0) {
461       return new BigNumber((byte) 0, big_decimal.abs());
462     }
463     else if (number_state == NEG_INF_STATE) {
464       return new BigNumber(POS_INF_STATE, null);
465     }
466     else {
467       return new BigNumber(number_state, null);
468     }
469   }
470   
471   public int signum() {
472     if (number_state == 0) {
473       return big_decimal.signum();
474     }
475     else if (number_state == NEG_INF_STATE) {
476       return -1;
477     }
478     else {
479       return 1;
480     }
481   }
482
483   public BigNumber setScale(int d, int round_enum) {
484     if (number_state == 0) {
485       return new BigNumber((byte) 0, big_decimal.setScale(d, round_enum));
486     }
487     // Can't round -inf, +inf and NaN
488
return this;
489   }
490
491   public BigNumber sqrt() {
492     double d = doubleValue();
493     d = Math.sqrt(d);
494     return fromDouble(d);
495   }
496
497
498
499
500
501   // ---------- Casting from java types ----------
502

503   /**
504    * Creates a BigNumber from a double.
505    */

506   public static BigNumber fromDouble(double value) {
507     if (value == Double.NEGATIVE_INFINITY) {
508       return NEGATIVE_INFINITY;
509     }
510     else if (value == Double.POSITIVE_INFINITY) {
511       return POSITIVE_INFINITY;
512     }
513     else if (value != value) {
514       return NaN;
515     }
516     return new BigNumber((byte) 0, new BigDecimal JavaDoc(Double.toString(value)));
517   }
518       
519   /**
520    * Creates a BigNumber from a float.
521    */

522   public static BigNumber fromFloat(float value) {
523     if (value == Float.NEGATIVE_INFINITY) {
524       return NEGATIVE_INFINITY;
525     }
526     else if (value == Float.POSITIVE_INFINITY) {
527       return POSITIVE_INFINITY;
528     }
529     else if (value != value) {
530       return NaN;
531     }
532     return new BigNumber((byte) 0, new BigDecimal JavaDoc(Float.toString(value)));
533   }
534
535   /**
536    * Creates a BigNumber from a long.
537    */

538   public static BigNumber fromLong(long value) {
539     return new BigNumber((byte) 0, BigDecimal.valueOf(value));
540   }
541
542   /**
543    * Creates a BigNumber from an int.
544    */

545   public static BigNumber fromInt(int value) {
546     return new BigNumber((byte) 0, BigDecimal.valueOf(value));
547   }
548
549   /**
550    * Creates a BigNumber from a string.
551    */

552   public static BigNumber fromString(String JavaDoc str) {
553     if (str.equals("Infinity")) {
554       return POSITIVE_INFINITY;
555     }
556     else if (str.equals("-Infinity")) {
557       return NEGATIVE_INFINITY;
558     }
559     else if (str.equals("NaN")) {
560       return NaN;
561     }
562     else {
563       return new BigNumber((byte) 0, new BigDecimal JavaDoc(str));
564     }
565   }
566
567   /**
568    * Creates a BigNumber from a BigDecimal.
569    */

570   public static BigNumber fromBigDecimal(BigDecimal JavaDoc val) {
571     return new BigNumber((byte) 0, val);
572   }
573
574   /**
575    * Creates a BigNumber from the given data.
576    */

577   public static BigNumber fromData(byte[] buf, int scale, byte state) {
578     if (state == 0) {
579       // This inlines common numbers to save a bit of memory.
580
if (scale == 0 && buf.length == 1) {
581         if (buf[0] == 0) {
582           return BIG_NUMBER_ZERO;
583         }
584         else if (buf[0] == 1) {
585           return BIG_NUMBER_ONE;
586         }
587       }
588       return new BigNumber(buf, scale, state);
589     }
590     else if (state == NEG_INF_STATE) {
591       return NEGATIVE_INFINITY;
592     }
593     else if (state == POS_INF_STATE) {
594       return POSITIVE_INFINITY;
595     }
596     else if (state == NaN_STATE) {
597       return NaN;
598     }
599     else {
600       throw new Error JavaDoc("Unknown number state.");
601     }
602   }
603
604
605   /**
606    * Statics for negative infinity, positive infinity and NaN.
607    */

608   public static final BigNumber NEGATIVE_INFINITY =
609                                       new BigNumber(NEG_INF_STATE, null);
610   public static final BigNumber POSITIVE_INFINITY =
611                                       new BigNumber(POS_INF_STATE, null);
612   public static final BigNumber NaN = new BigNumber(NaN_STATE, null);
613
614   /**
615    * Statics for 0 and 1.
616    */

617   public static final BigNumber BIG_NUMBER_ZERO = BigNumber.fromLong(0);
618   public static final BigNumber BIG_NUMBER_ONE = BigNumber.fromLong(1);
619
620 }
621
Popular Tags