1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package java.math;
19 
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.io.Serializable;
23 import java.io.StreamCorruptedException;
24 
25 /**
26  * Immutable objects describing settings such as rounding mode and digit
27  * precision for the numerical operations provided by class {@link BigDecimal}.
28  */
29 public final class MathContext implements Serializable {
30     private static final long serialVersionUID = 5579720004786848255L;
31 
32     /**
33      * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> quadruple
34      * decimal precision format: 34 digit precision and
35      * {@link RoundingMode#HALF_EVEN} rounding.
36      */
37     public static final MathContext DECIMAL128 = new MathContext(34, RoundingMode.HALF_EVEN);
38 
39     /**
40      * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> single decimal
41      * precision format: 7 digit precision and {@link RoundingMode#HALF_EVEN}
42      * rounding.
43      */
44     public static final MathContext DECIMAL32 = new MathContext(7, RoundingMode.HALF_EVEN);
45 
46     /**
47      * A {@code MathContext} which corresponds to the <a href="http://en.wikipedia.org/wiki/IEEE_754-1985">IEEE 754</a> double decimal
48      * precision format: 16 digit precision and {@link RoundingMode#HALF_EVEN}
49      * rounding.
50      */
51     public static final MathContext DECIMAL64 = new MathContext(16, RoundingMode.HALF_EVEN);
52 
53     /**
54      * A {@code MathContext} for unlimited precision with
55      * {@link RoundingMode#HALF_UP} rounding.
56      */
57     public static final MathContext UNLIMITED = new MathContext(0, RoundingMode.HALF_UP);
58 
59     /**
60      * The number of digits to be used for an operation; results are rounded to
61      * this precision.
62      */
63     private final int precision;
64 
65     /**
66      * A {@code RoundingMode} object which specifies the algorithm to be used
67      * for rounding.
68      */
69     private final RoundingMode roundingMode;
70 
71     /**
72      * Constructs a new {@code MathContext} with the specified precision and
73      * with the rounding mode {@link RoundingMode#HALF_UP HALF_UP}. If the
74      * precision passed is zero, then this implies that the computations have to
75      * be performed exact, the rounding mode in this case is irrelevant.
76      *
77      * @param precision
78      *            the precision for the new {@code MathContext}.
79      * @throws IllegalArgumentException
80      *             if {@code precision < 0}.
81      */
MathContext(int precision)82     public MathContext(int precision) {
83         this(precision, RoundingMode.HALF_UP);
84     }
85 
86     /**
87      * Constructs a new {@code MathContext} with the specified precision and
88      * with the specified rounding mode. If the precision passed is zero, then
89      * this implies that the computations have to be performed exact, the
90      * rounding mode in this case is irrelevant.
91      *
92      * @param precision
93      *            the precision for the new {@code MathContext}.
94      * @param roundingMode
95      *            the rounding mode for the new {@code MathContext}.
96      * @throws IllegalArgumentException
97      *             if {@code precision < 0}.
98      * @throws NullPointerException
99      *             if {@code roundingMode} is {@code null}.
100      */
MathContext(int precision, RoundingMode roundingMode)101     public MathContext(int precision, RoundingMode roundingMode) {
102         this.precision = precision;
103         this.roundingMode = roundingMode;
104         checkValid();
105     }
106 
107     /**
108      * Constructs a new {@code MathContext} from a string. The string has to
109      * specify the precision and the rounding mode to be used and has to follow
110      * the following syntax: "precision=&lt;precision&gt; roundingMode=&lt;roundingMode&gt;"
111      * This is the same form as the one returned by the {@link #toString}
112      * method.
113      *
114      * @throws IllegalArgumentException
115      *             if the string is not in the correct format or if the
116      *             precision specified is < 0.
117      */
MathContext(String s)118     public MathContext(String s) {
119         int precisionLength = "precision=".length();
120         int roundingModeLength = "roundingMode=".length();
121 
122         int spaceIndex;
123         if (!s.startsWith("precision=") || (spaceIndex = s.indexOf(' ', precisionLength)) == -1) {
124             throw invalidMathContext("Missing precision", s);
125         }
126         String precisionString = s.substring(precisionLength, spaceIndex);
127         try {
128             this.precision = Integer.parseInt(precisionString);
129         } catch (NumberFormatException nfe) {
130             throw invalidMathContext("Bad precision", s);
131         }
132 
133         int roundingModeStart = spaceIndex + 1;
134         if (!s.regionMatches(roundingModeStart, "roundingMode=", 0, roundingModeLength)) {
135             throw invalidMathContext("Missing rounding mode", s);
136         }
137         roundingModeStart += roundingModeLength;
138         this.roundingMode = RoundingMode.valueOf(s.substring(roundingModeStart));
139 
140         checkValid();
141     }
142 
invalidMathContext(String reason, String s)143     private IllegalArgumentException invalidMathContext(String reason, String s) {
144         throw new IllegalArgumentException(reason + ": " + s);
145     }
146 
checkValid()147     private void checkValid() {
148         if (precision < 0) {
149             throw new IllegalArgumentException("Negative precision: " + precision);
150         }
151         if (roundingMode == null) {
152             throw new NullPointerException("roundingMode == null");
153         }
154     }
155 
156     /**
157      * Returns the precision. The precision is the number of digits used for an
158      * operation. Results are rounded to this precision. The precision is
159      * guaranteed to be non negative. If the precision is zero, then the
160      * computations have to be performed exact, results are not rounded in this
161      * case.
162      *
163      * @return the precision.
164      */
getPrecision()165     public int getPrecision() {
166         return precision;
167     }
168 
169     /**
170      * Returns the rounding mode. The rounding mode is the strategy to be used
171      * to round results.
172      * <p>
173      * The rounding mode is one of
174      * {@link RoundingMode#UP},
175      * {@link RoundingMode#DOWN},
176      * {@link RoundingMode#CEILING},
177      * {@link RoundingMode#FLOOR},
178      * {@link RoundingMode#HALF_UP},
179      * {@link RoundingMode#HALF_DOWN},
180      * {@link RoundingMode#HALF_EVEN}, or
181      * {@link RoundingMode#UNNECESSARY}.
182      *
183      * @return the rounding mode.
184      */
getRoundingMode()185     public RoundingMode getRoundingMode() {
186         return roundingMode;
187     }
188 
189     /**
190      * Returns true if x is a {@code MathContext} with the same precision
191      * setting and the same rounding mode as this {@code MathContext} instance.
192      *
193      * @param x
194      *            object to be compared.
195      * @return {@code true} if this {@code MathContext} instance is equal to the
196      *         {@code x} argument; {@code false} otherwise.
197      */
198     @Override
equals(Object x)199     public boolean equals(Object x) {
200         return ((x instanceof MathContext)
201                 && (((MathContext) x).getPrecision() == precision) && (((MathContext) x)
202                 .getRoundingMode() == roundingMode));
203     }
204 
205     /**
206      * Returns the hash code for this {@code MathContext} instance.
207      *
208      * @return the hash code for this {@code MathContext}.
209      */
210     @Override
hashCode()211     public int hashCode() {
212         // Make place for the necessary bits to represent 8 rounding modes
213         return ((precision << 3) | roundingMode.ordinal());
214     }
215 
216     /**
217      * Returns the string representation for this {@code MathContext} instance.
218      * The string has the form
219      * {@code
220      * "precision=<precision> roundingMode=<roundingMode>"
221      * } where {@code <precision>} is an integer describing the number
222      * of digits used for operations and {@code <roundingMode>} is the
223      * string representation of the rounding mode.
224      *
225      * @return a string representation for this {@code MathContext} instance
226      */
227     @Override
toString()228     public String toString() {
229         return "precision=" + precision + " roundingMode=" + roundingMode;
230     }
231 
232     /**
233      * Makes checks upon deserialization of a {@code MathContext} instance.
234      * Checks whether {@code precision >= 0} and {@code roundingMode != null}
235      *
236      * @throws StreamCorruptedException
237      *             if {@code precision < 0}
238      * @throws StreamCorruptedException
239      *             if {@code roundingMode == null}
240      */
readObject(ObjectInputStream s)241     private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
242         s.defaultReadObject();
243         try {
244             checkValid();
245         } catch (Exception ex) {
246             throw new StreamCorruptedException(ex.getMessage());
247         }
248     }
249 }
250