SimpleBigDecimal.java revision e6bf3e8dfa2804891a82075cb469b736321b4827
1package org.bouncycastle.math.ec;
2
3import java.math.BigInteger;
4
5/**
6 * Class representing a simple version of a big decimal. A
7 * <code>SimpleBigDecimal</code> is basically a
8 * {@link java.math.BigInteger BigInteger} with a few digits on the right of
9 * the decimal point. The number of (binary) digits on the right of the decimal
10 * point is called the <code>scale</code> of the <code>SimpleBigDecimal</code>.
11 * Unlike in {@link java.math.BigDecimal BigDecimal}, the scale is not adjusted
12 * automatically, but must be set manually. All <code>SimpleBigDecimal</code>s
13 * taking part in the same arithmetic operation must have equal scale. The
14 * result of a multiplication of two <code>SimpleBigDecimal</code>s returns a
15 * <code>SimpleBigDecimal</code> with double scale.
16 */
17class SimpleBigDecimal
18    //extends Number   // not in J2ME - add compatibility class?
19{
20    private static final long serialVersionUID = 1L;
21
22    private final BigInteger bigInt;
23    private final int scale;
24
25    /**
26     * Returns a <code>SimpleBigDecimal</code> representing the same numerical
27     * value as <code>value</code>.
28     * @param value The value of the <code>SimpleBigDecimal</code> to be
29     * created.
30     * @param scale The scale of the <code>SimpleBigDecimal</code> to be
31     * created.
32     * @return The such created <code>SimpleBigDecimal</code>.
33     */
34    public static SimpleBigDecimal getInstance(BigInteger value, int scale)
35    {
36        return new SimpleBigDecimal(value.shiftLeft(scale), scale);
37    }
38
39    /**
40     * Constructor for <code>SimpleBigDecimal</code>. The value of the
41     * constructed <code>SimpleBigDecimal</code> equals <code>bigInt /
42     * 2<sup>scale</sup></code>.
43     * @param bigInt The <code>bigInt</code> value parameter.
44     * @param scale The scale of the constructed <code>SimpleBigDecimal</code>.
45     */
46    public SimpleBigDecimal(BigInteger bigInt, int scale)
47    {
48        if (scale < 0)
49        {
50            throw new IllegalArgumentException("scale may not be negative");
51        }
52
53        this.bigInt = bigInt;
54        this.scale = scale;
55    }
56
57    private SimpleBigDecimal(SimpleBigDecimal limBigDec)
58    {
59        bigInt = limBigDec.bigInt;
60        scale = limBigDec.scale;
61    }
62
63    private void checkScale(SimpleBigDecimal b)
64    {
65        if (scale != b.scale)
66        {
67            throw new IllegalArgumentException("Only SimpleBigDecimal of " +
68                "same scale allowed in arithmetic operations");
69        }
70    }
71
72    public SimpleBigDecimal adjustScale(int newScale)
73    {
74        if (newScale < 0)
75        {
76            throw new IllegalArgumentException("scale may not be negative");
77        }
78
79        if (newScale == scale)
80        {
81            return new SimpleBigDecimal(this);
82        }
83
84        return new SimpleBigDecimal(bigInt.shiftLeft(newScale - scale),
85                newScale);
86    }
87
88    public SimpleBigDecimal add(SimpleBigDecimal b)
89    {
90        checkScale(b);
91        return new SimpleBigDecimal(bigInt.add(b.bigInt), scale);
92    }
93
94    public SimpleBigDecimal add(BigInteger b)
95    {
96        return new SimpleBigDecimal(bigInt.add(b.shiftLeft(scale)), scale);
97    }
98
99    public SimpleBigDecimal negate()
100    {
101        return new SimpleBigDecimal(bigInt.negate(), scale);
102    }
103
104    public SimpleBigDecimal subtract(SimpleBigDecimal b)
105    {
106        return add(b.negate());
107    }
108
109    public SimpleBigDecimal subtract(BigInteger b)
110    {
111        return new SimpleBigDecimal(bigInt.subtract(b.shiftLeft(scale)),
112                scale);
113    }
114
115    public SimpleBigDecimal multiply(SimpleBigDecimal b)
116    {
117        checkScale(b);
118        return new SimpleBigDecimal(bigInt.multiply(b.bigInt), scale + scale);
119    }
120
121    public SimpleBigDecimal multiply(BigInteger b)
122    {
123        return new SimpleBigDecimal(bigInt.multiply(b), scale);
124    }
125
126    public SimpleBigDecimal divide(SimpleBigDecimal b)
127    {
128        checkScale(b);
129        BigInteger dividend = bigInt.shiftLeft(scale);
130        return new SimpleBigDecimal(dividend.divide(b.bigInt), scale);
131    }
132
133    public SimpleBigDecimal divide(BigInteger b)
134    {
135        return new SimpleBigDecimal(bigInt.divide(b), scale);
136    }
137
138    public SimpleBigDecimal shiftLeft(int n)
139    {
140        return new SimpleBigDecimal(bigInt.shiftLeft(n), scale);
141    }
142
143    public int compareTo(SimpleBigDecimal val)
144    {
145        checkScale(val);
146        return bigInt.compareTo(val.bigInt);
147    }
148
149    public int compareTo(BigInteger val)
150    {
151        return bigInt.compareTo(val.shiftLeft(scale));
152    }
153
154    public BigInteger floor()
155    {
156        return bigInt.shiftRight(scale);
157    }
158
159    public BigInteger round()
160    {
161        SimpleBigDecimal oneHalf = new SimpleBigDecimal(ECConstants.ONE, 1);
162        return add(oneHalf.adjustScale(scale)).floor();
163    }
164
165    public int intValue()
166    {
167        return floor().intValue();
168    }
169
170    public long longValue()
171    {
172        return floor().longValue();
173    }
174          /* NON-J2ME compliant.
175    public double doubleValue()
176    {
177        return Double.valueOf(toString()).doubleValue();
178    }
179
180    public float floatValue()
181    {
182        return Float.valueOf(toString()).floatValue();
183    }
184       */
185    public int getScale()
186    {
187        return scale;
188    }
189
190    public String toString()
191    {
192        if (scale == 0)
193        {
194            return bigInt.toString();
195        }
196
197        BigInteger floorBigInt = floor();
198
199        BigInteger fract = bigInt.subtract(floorBigInt.shiftLeft(scale));
200        if (bigInt.signum() == -1)
201        {
202            fract = ECConstants.ONE.shiftLeft(scale).subtract(fract);
203        }
204
205        if ((floorBigInt.signum() == -1) && (!(fract.equals(ECConstants.ZERO))))
206        {
207            floorBigInt = floorBigInt.add(ECConstants.ONE);
208        }
209        String leftOfPoint = floorBigInt.toString();
210
211        char[] fractCharArr = new char[scale];
212        String fractStr = fract.toString(2);
213        int fractLen = fractStr.length();
214        int zeroes = scale - fractLen;
215        for (int i = 0; i < zeroes; i++)
216        {
217            fractCharArr[i] = '0';
218        }
219        for (int j = 0; j < fractLen; j++)
220        {
221            fractCharArr[zeroes + j] = fractStr.charAt(j);
222        }
223        String rightOfPoint = new String(fractCharArr);
224
225        StringBuffer sb = new StringBuffer(leftOfPoint);
226        sb.append(".");
227        sb.append(rightOfPoint);
228
229        return sb.toString();
230    }
231
232    public boolean equals(Object o)
233    {
234        if (this == o)
235        {
236            return true;
237        }
238
239        if (!(o instanceof SimpleBigDecimal))
240        {
241            return false;
242        }
243
244        SimpleBigDecimal other = (SimpleBigDecimal)o;
245        return ((bigInt.equals(other.bigInt)) && (scale == other.scale));
246    }
247
248    public int hashCode()
249    {
250        return bigInt.hashCode() ^ scale;
251    }
252
253}
254