001: /**
002: * com.mckoi.util.BigNumber 26 Jul 2002
003: *
004: * Mckoi SQL Database ( http://www.mckoi.com/database )
005: * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * Version 2 as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License Version 2 for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * Version 2 along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: * Change Log:
021: *
022: *
023: */package com.mckoi.util;
024:
025: import java.math.BigDecimal;
026: import java.math.BigInteger;
027:
028: /**
029: * Extends BigDecimal to allow a number to be positive infinity, negative
030: * infinity and not-a-number. This provides compatibility with float and
031: * double types.
032: *
033: * @author Tobias Downer
034: */
035:
036: public final class BigNumber extends Number {
037:
038: static final long serialVersionUID = -8681578742639638105L;
039:
040: /**
041: * State enumerations.
042: */
043: private final static byte NEG_INF_STATE = 1;
044: private final static byte POS_INF_STATE = 2;
045: private final static byte NaN_STATE = 3;
046:
047: /**
048: * The state of the number, either 0 for number is the BigDecimal, 1 for
049: * negative infinity, 2 for positive infinity and 3 for NaN.
050: */
051: private byte number_state;
052:
053: /**
054: * The BigDecimal representation.
055: */
056: private BigDecimal big_decimal;
057:
058: /**
059: * A 'long' representation of this number.
060: */
061: private long long_representation;
062:
063: /**
064: * If this can be represented as an int or long, this contains the number
065: * of bytes needed to represent the number.
066: */
067: private byte byte_count = 120;
068:
069: /**
070: * Constructs the number.
071: */
072: private BigNumber(byte number_state, BigDecimal big_decimal) {
073: this .number_state = number_state;
074: if (number_state == 0) {
075: setBigDecimal(big_decimal);
076: }
077: }
078:
079: private BigNumber(byte[] buf, int scale, byte state) {
080: this .number_state = state;
081: if (number_state == 0) {
082: BigInteger bigint = new BigInteger(buf);
083: setBigDecimal(new BigDecimal(bigint, scale));
084: }
085: }
086:
087: // Only call this from a constructor!
088: private void setBigDecimal(BigDecimal big_decimal) {
089: this .big_decimal = big_decimal;
090: if (big_decimal.scale() == 0) {
091: BigInteger bint = big_decimal.toBigInteger();
092: int bit_count = big_decimal.toBigInteger().bitLength();
093: if (bit_count < 30) {
094: this .long_representation = bint.longValue();
095: this .byte_count = 4;
096: } else if (bit_count < 60) {
097: this .long_representation = bint.longValue();
098: this .byte_count = 8;
099: ;
100: }
101: }
102: }
103:
104: /**
105: * Returns true if this BigNumber can be represented by a 64-bit long (has
106: * no scale).
107: */
108: public boolean canBeRepresentedAsLong() {
109: return byte_count <= 8;
110: }
111:
112: /**
113: * Returns true if this BigNumber can be represented by a 32-bit int (has
114: * no scale).
115: */
116: public boolean canBeRepresentedAsInt() {
117: return byte_count <= 4;
118: }
119:
120: /**
121: * Returns the scale of this number, or -1 if the number has no scale (if
122: * it -inf, +inf or NaN).
123: */
124: public int getScale() {
125: if (number_state == 0) {
126: return big_decimal.scale();
127: } else {
128: return -1;
129: }
130: }
131:
132: /**
133: * Returns the state of this number. Returns either 1 which indicates
134: * negative infinity, 2 which indicates positive infinity, or 3 which
135: * indicates NaN.
136: */
137: public byte getState() {
138: return number_state;
139: }
140:
141: /**
142: * Returns the inverse of the state.
143: */
144: private byte getInverseState() {
145: if (number_state == NEG_INF_STATE) {
146: return POS_INF_STATE;
147: } else if (number_state == POS_INF_STATE) {
148: return NEG_INF_STATE;
149: } else {
150: return number_state;
151: }
152: }
153:
154: /**
155: * Returns this number as a byte array (unscaled).
156: */
157: public byte[] toByteArray() {
158: if (number_state == 0) {
159: return big_decimal.movePointRight(big_decimal.scale())
160: .toBigInteger().toByteArray();
161: // [ NOTE: The following code is 1.2+ only but BigNumber should be compatible
162: // with 1.1 so we use the above call ]
163: // return big_decimal.unscaledValue().toByteArray();
164: } else {
165: return new byte[0];
166: }
167: }
168:
169: /**
170: * Returns this big number as a string.
171: */
172: public String toString() {
173: switch (number_state) {
174: case (0):
175: return big_decimal.toString();
176: case (NEG_INF_STATE):
177: return "-Infinity";
178: case (POS_INF_STATE):
179: return "Infinity";
180: case (NaN_STATE):
181: return "NaN";
182: default:
183: throw new Error("Unknown number state");
184: }
185: }
186:
187: /**
188: * Returns this big number as a double.
189: */
190: public double doubleValue() {
191: switch (number_state) {
192: case (0):
193: return big_decimal.doubleValue();
194: case (NEG_INF_STATE):
195: return Double.NEGATIVE_INFINITY;
196: case (POS_INF_STATE):
197: return Double.POSITIVE_INFINITY;
198: case (NaN_STATE):
199: return Double.NaN;
200: default:
201: throw new Error("Unknown number state");
202: }
203: }
204:
205: /**
206: * Returns this big number as a float.
207: */
208: public float floatValue() {
209: switch (number_state) {
210: case (0):
211: return big_decimal.floatValue();
212: case (NEG_INF_STATE):
213: return Float.NEGATIVE_INFINITY;
214: case (POS_INF_STATE):
215: return Float.POSITIVE_INFINITY;
216: case (NaN_STATE):
217: return Float.NaN;
218: default:
219: throw new Error("Unknown number state");
220: }
221: }
222:
223: /**
224: * Returns this big number as a long.
225: */
226: public long longValue() {
227: if (canBeRepresentedAsLong()) {
228: return long_representation;
229: }
230: switch (number_state) {
231: case (0):
232: return big_decimal.longValue();
233: default:
234: return (long) doubleValue();
235: }
236: }
237:
238: /**
239: * Returns this big number as an int.
240: */
241: public int intValue() {
242: if (canBeRepresentedAsLong()) {
243: return (int) long_representation;
244: }
245: switch (number_state) {
246: case (0):
247: return big_decimal.intValue();
248: default:
249: return (int) doubleValue();
250: }
251: }
252:
253: /**
254: * Returns this big number as a short.
255: */
256: public short shortValue() {
257: return (short) intValue();
258: }
259:
260: /**
261: * Returns this big number as a byte.
262: */
263: public byte byteValue() {
264: return (byte) intValue();
265: }
266:
267: /**
268: * Returns the big number as a BigDecimal object. Note that this throws
269: * an arith error if this number represents NaN, +Inf or -Inf.
270: */
271: public BigDecimal asBigDecimal() {
272: if (number_state == 0) {
273: return big_decimal;
274: } else {
275: throw new ArithmeticException(
276: "NaN, +Infinity or -Infinity can't be translated to a BigDecimal");
277: }
278: }
279:
280: /**
281: * Compares this BigNumber with the given BigNumber. Returns 0 if the values
282: * are equal, >0 if this is greater than the given value, and < 0 if this
283: * is less than the given value.
284: */
285: public int compareTo(BigNumber number) {
286:
287: if (this == number) {
288: return 0;
289: }
290:
291: // If this is a non-infinity number
292: if (number_state == 0) {
293:
294: // If both values can be represented by a long value
295: if (canBeRepresentedAsLong()
296: && number.canBeRepresentedAsLong()) {
297: // Perform a long comparison check,
298: if (long_representation > number.long_representation) {
299: return 1;
300: } else if (long_representation < number.long_representation) {
301: return -1;
302: } else {
303: return 0;
304: }
305:
306: }
307:
308: // And the compared number is non-infinity then use the BigDecimal
309: // compareTo method.
310: if (number.number_state == 0) {
311: return big_decimal.compareTo(number.big_decimal);
312: } else {
313: // Comparing a regular number with a NaN number.
314: // If positive infinity or if NaN
315: if (number.number_state == POS_INF_STATE
316: || number.number_state == NaN_STATE) {
317: return -1;
318: }
319: // If negative infinity
320: else if (number.number_state == NEG_INF_STATE) {
321: return 1;
322: } else {
323: throw new Error("Unknown number state.");
324: }
325: }
326: } else {
327: // This number is a NaN number.
328: // Are we comparing with a regular number?
329: if (number.number_state == 0) {
330: // Yes, negative infinity
331: if (number_state == NEG_INF_STATE) {
332: return -1;
333: }
334: // positive infinity or NaN
335: else if (number_state == POS_INF_STATE
336: || number_state == NaN_STATE) {
337: return 1;
338: } else {
339: throw new Error("Unknown number state.");
340: }
341: } else {
342: // Comparing NaN number with a NaN number.
343: // This compares -Inf less than Inf and NaN and NaN greater than
344: // Inf and -Inf. -Inf < Inf < NaN
345: return (int) (number_state - number.number_state);
346: }
347: }
348: }
349:
350: /**
351: * The equals comparison uses the BigDecimal 'equals' method to compare
352: * values. This means that '0' is NOT equal to '0.0' and '10.0' is NOT equal
353: * to '10.00'. Care should be taken when using this method.
354: */
355: public boolean equals(Object ob) {
356: BigNumber bnum = (BigNumber) ob;
357: if (number_state != 0) {
358: return (number_state == bnum.number_state);
359: } else {
360: return big_decimal.equals(bnum.big_decimal);
361: }
362: }
363:
364: /**
365: * Statics.
366: */
367: private final static BigDecimal BD_ZERO = new BigDecimal(0);
368:
369: // ---- Mathematical functions ----
370:
371: public BigNumber bitWiseOr(BigNumber number) {
372: if (number_state == 0 && getScale() == 0
373: && number.number_state == 0 && number.getScale() == 0) {
374: BigInteger bi1 = big_decimal.toBigInteger();
375: BigInteger bi2 = number.big_decimal.toBigInteger();
376: return new BigNumber((byte) 0, new BigDecimal(bi1.or(bi2)));
377: } else {
378: return null;
379: }
380: }
381:
382: public BigNumber add(BigNumber number) {
383: if (number_state == 0) {
384: if (number.number_state == 0) {
385: return new BigNumber((byte) 0, big_decimal
386: .add(number.big_decimal));
387: } else {
388: return new BigNumber(number.number_state, null);
389: }
390: } else {
391: return new BigNumber(number_state, null);
392: }
393: }
394:
395: public BigNumber subtract(BigNumber number) {
396: if (number_state == 0) {
397: if (number.number_state == 0) {
398: return new BigNumber((byte) 0, big_decimal
399: .subtract(number.big_decimal));
400: } else {
401: return new BigNumber(number.getInverseState(), null);
402: }
403: } else {
404: return new BigNumber(number_state, null);
405: }
406: }
407:
408: public BigNumber multiply(BigNumber number) {
409: if (number_state == 0) {
410: if (number.number_state == 0) {
411: return new BigNumber((byte) 0, big_decimal
412: .multiply(number.big_decimal));
413: } else {
414: return new BigNumber(number.number_state, null);
415: }
416: } else {
417: return new BigNumber(number_state, null);
418: }
419: }
420:
421: public BigNumber divide(BigNumber number) {
422: if (number_state == 0) {
423: if (number.number_state == 0) {
424: BigDecimal div_by = number.big_decimal;
425: if (div_by.compareTo(BD_ZERO) != 0) {
426: return new BigNumber((byte) 0, big_decimal.divide(
427: div_by, 10, BigDecimal.ROUND_HALF_UP));
428: }
429: }
430: }
431: // Return NaN if we can't divide
432: return new BigNumber((byte) 3, null);
433: }
434:
435: public BigNumber abs() {
436: if (number_state == 0) {
437: return new BigNumber((byte) 0, big_decimal.abs());
438: } else if (number_state == NEG_INF_STATE) {
439: return new BigNumber(POS_INF_STATE, null);
440: } else {
441: return new BigNumber(number_state, null);
442: }
443: }
444:
445: public int signum() {
446: if (number_state == 0) {
447: return big_decimal.signum();
448: } else if (number_state == NEG_INF_STATE) {
449: return -1;
450: } else {
451: return 1;
452: }
453: }
454:
455: public BigNumber setScale(int d, int round_enum) {
456: if (number_state == 0) {
457: return new BigNumber((byte) 0, big_decimal.setScale(d,
458: round_enum));
459: }
460: // Can't round -inf, +inf and NaN
461: return this ;
462: }
463:
464: public BigNumber sqrt() {
465: double d = doubleValue();
466: d = Math.sqrt(d);
467: return fromDouble(d);
468: }
469:
470: // ---------- Casting from java types ----------
471:
472: /**
473: * Creates a BigNumber from a double.
474: */
475: public static BigNumber fromDouble(double value) {
476: if (value == Double.NEGATIVE_INFINITY) {
477: return NEGATIVE_INFINITY;
478: } else if (value == Double.POSITIVE_INFINITY) {
479: return POSITIVE_INFINITY;
480: } else if (value != value) {
481: return NaN;
482: }
483: return new BigNumber((byte) 0, new BigDecimal(Double
484: .toString(value)));
485: }
486:
487: /**
488: * Creates a BigNumber from a float.
489: */
490: public static BigNumber fromFloat(float value) {
491: if (value == Float.NEGATIVE_INFINITY) {
492: return NEGATIVE_INFINITY;
493: } else if (value == Float.POSITIVE_INFINITY) {
494: return POSITIVE_INFINITY;
495: } else if (value != value) {
496: return NaN;
497: }
498: return new BigNumber((byte) 0, new BigDecimal(Float
499: .toString(value)));
500: }
501:
502: /**
503: * Creates a BigNumber from a long.
504: */
505: public static BigNumber fromLong(long value) {
506: return new BigNumber((byte) 0, BigDecimal.valueOf(value));
507: }
508:
509: /**
510: * Creates a BigNumber from an int.
511: */
512: public static BigNumber fromInt(int value) {
513: return new BigNumber((byte) 0, BigDecimal.valueOf(value));
514: }
515:
516: /**
517: * Creates a BigNumber from a string.
518: */
519: public static BigNumber fromString(String str) {
520: if (str.equals("Infinity")) {
521: return POSITIVE_INFINITY;
522: } else if (str.equals("-Infinity")) {
523: return NEGATIVE_INFINITY;
524: } else if (str.equals("NaN")) {
525: return NaN;
526: } else {
527: return new BigNumber((byte) 0, new BigDecimal(str));
528: }
529: }
530:
531: /**
532: * Creates a BigNumber from a BigDecimal.
533: */
534: public static BigNumber fromBigDecimal(BigDecimal val) {
535: return new BigNumber((byte) 0, val);
536: }
537:
538: /**
539: * Creates a BigNumber from the given data.
540: */
541: public static BigNumber fromData(byte[] buf, int scale, byte state) {
542: if (state == 0) {
543: // This inlines common numbers to save a bit of memory.
544: if (scale == 0 && buf.length == 1) {
545: if (buf[0] == 0) {
546: return BIG_NUMBER_ZERO;
547: } else if (buf[0] == 1) {
548: return BIG_NUMBER_ONE;
549: }
550: }
551: return new BigNumber(buf, scale, state);
552: } else if (state == NEG_INF_STATE) {
553: return NEGATIVE_INFINITY;
554: } else if (state == POS_INF_STATE) {
555: return POSITIVE_INFINITY;
556: } else if (state == NaN_STATE) {
557: return NaN;
558: } else {
559: throw new Error("Unknown number state.");
560: }
561: }
562:
563: /**
564: * Statics for negative infinity, positive infinity and NaN.
565: */
566: public static final BigNumber NEGATIVE_INFINITY = new BigNumber(
567: NEG_INF_STATE, null);
568: public static final BigNumber POSITIVE_INFINITY = new BigNumber(
569: POS_INF_STATE, null);
570: public static final BigNumber NaN = new BigNumber(NaN_STATE, null);
571:
572: /**
573: * Statics for 0 and 1.
574: */
575: public static final BigNumber BIG_NUMBER_ZERO = BigNumber
576: .fromLong(0);
577: public static final BigNumber BIG_NUMBER_ONE = BigNumber
578: .fromLong(1);
579:
580: }
|