View Javadoc
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 org.apache.commons.math4.legacy.core.dfp;
19  
20  /** Subclass of {@link Dfp} which hides the radix-10000 artifacts of the superclass.
21   * This should give outward appearances of being a decimal number with DIGITS*4-3
22   * decimal digits. This class can be subclassed to appear to be an arbitrary number
23   * of decimal digits less than DIGITS*4-3.
24   * @since 2.2
25   */
26  public class DfpDec extends Dfp {
27  
28      /** Makes an instance with a value of zero.
29       * @param factory factory linked to this instance
30       */
31      protected DfpDec(final DfpField factory) {
32          super(factory);
33      }
34  
35      /** Create an instance from a byte value.
36       * @param factory factory linked to this instance
37       * @param x value to convert to an instance
38       */
39      protected DfpDec(final DfpField factory, byte x) {
40          super(factory, x);
41      }
42  
43      /** Create an instance from an int value.
44       * @param factory factory linked to this instance
45       * @param x value to convert to an instance
46       */
47      protected DfpDec(final DfpField factory, int x) {
48          super(factory, x);
49      }
50  
51      /** Create an instance from a long value.
52       * @param factory factory linked to this instance
53       * @param x value to convert to an instance
54       */
55      protected DfpDec(final DfpField factory, long x) {
56          super(factory, x);
57      }
58  
59      /** Create an instance from a double value.
60       * @param factory factory linked to this instance
61       * @param x value to convert to an instance
62       */
63      protected DfpDec(final DfpField factory, double x) {
64          super(factory, x);
65          round(0);
66      }
67  
68      /** Copy constructor.
69       * @param d instance to copy
70       */
71      public DfpDec(final Dfp d) {
72          super(d);
73          round(0);
74      }
75  
76      /** Create an instance from a String representation.
77       * @param factory factory linked to this instance
78       * @param s string representation of the instance
79       */
80      protected DfpDec(final DfpField factory, final String s) {
81          super(factory, s);
82          round(0);
83      }
84  
85      /** Creates an instance with a non-finite value.
86       * @param factory factory linked to this instance
87       * @param sign sign of the Dfp to create
88       * @param nans code of the value, must be one of {@link #INFINITE},
89       * {@link #SNAN},  {@link #QNAN}
90       */
91      protected DfpDec(final DfpField factory, final byte sign, final byte nans) {
92          super(factory, sign, nans);
93      }
94  
95      /** {@inheritDoc} */
96      @Override
97      public Dfp newInstance() {
98          return new DfpDec(getField());
99      }
100 
101     /** {@inheritDoc} */
102     @Override
103     public Dfp newInstance(final byte x) {
104         return new DfpDec(getField(), x);
105     }
106 
107     /** {@inheritDoc} */
108     @Override
109     public Dfp newInstance(final int x) {
110         return new DfpDec(getField(), x);
111     }
112 
113     /** {@inheritDoc} */
114     @Override
115     public Dfp newInstance(final long x) {
116         return new DfpDec(getField(), x);
117     }
118 
119     /** {@inheritDoc} */
120     @Override
121     public Dfp newInstance(final double x) {
122         return new DfpDec(getField(), x);
123     }
124 
125     /** {@inheritDoc} */
126     @Override
127     public Dfp newInstance(final Dfp d) {
128 
129         // make sure we don't mix number with different precision
130         if (getField().getRadixDigits() != d.getField().getRadixDigits()) {
131             getField().setIEEEFlagsBits(DfpField.FLAG_INVALID);
132             final Dfp result = newInstance(getZero());
133             result.nans = QNAN;
134             return dotrap(DfpField.FLAG_INVALID, "newInstance", d, result);
135         }
136 
137         return new DfpDec(d);
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     public Dfp newInstance(final String s) {
143         return new DfpDec(getField(), s);
144     }
145 
146     /** {@inheritDoc} */
147     @Override
148     public Dfp newInstance(final byte sign, final byte nans) {
149         return new DfpDec(getField(), sign, nans);
150     }
151 
152     /** Get the number of decimal digits this class is going to represent.
153      * Default implementation returns {@link #getRadixDigits()}*4-3. Subclasses can
154      * override this to return something less.
155      * @return number of decimal digits this class is going to represent
156      */
157     protected int getDecimalDigits() {
158         return getRadixDigits() * 4 - 3;
159     }
160 
161     /** {@inheritDoc} */
162     @Override
163     protected int round(int in) {
164 
165         int msb = mant[mant.length - 1];
166         if (msb == 0) {
167             // special case -- this == zero
168             return 0;
169         }
170 
171         int cmaxdigits = mant.length * 4;
172         int lsbthreshold = 1000;
173         while (lsbthreshold > msb) {
174             lsbthreshold /= 10;
175             cmaxdigits--;
176         }
177 
178 
179         final int digits = getDecimalDigits();
180         final int lsbshift = cmaxdigits - digits;
181         final int lsd = lsbshift / 4;
182 
183         lsbthreshold = 1;
184         for (int i = 0; i < lsbshift % 4; i++) {
185             lsbthreshold *= 10;
186         }
187 
188         final int lsb = mant[lsd];
189 
190         if (lsbthreshold <= 1 && digits == 4 * mant.length - 3) {
191             return super.round(in);
192         }
193 
194         int discarded = in;  // not looking at this after this point
195         final int n;
196         if (lsbthreshold == 1) {
197             // look to the next digit for rounding
198             n = (mant[lsd - 1] / 1000) % 10;
199             mant[lsd - 1] %= 1000;
200             discarded |= mant[lsd - 1];
201         } else {
202             n = (lsb * 10 / lsbthreshold) % 10;
203             discarded |= lsb % (lsbthreshold / 10);
204         }
205 
206         for (int i = 0; i < lsd; i++) {
207             discarded |= mant[i];    // need to know if there are any discarded bits
208             mant[i] = 0;
209         }
210 
211         mant[lsd] = lsb / lsbthreshold * lsbthreshold;
212 
213         final boolean inc;
214         switch (getField().getRoundingMode()) {
215         case ROUND_DOWN:
216             inc = false;
217             break;
218 
219         case ROUND_UP:
220             inc = (n != 0) || (discarded != 0); // round up if n!=0
221             break;
222 
223         case ROUND_HALF_UP:
224             inc = n >= 5;  // round half up
225             break;
226 
227         case ROUND_HALF_DOWN:
228             inc = n > 5;  // round half down
229             break;
230 
231         case ROUND_HALF_EVEN:
232             inc = (n > 5) ||
233                   (n == 5 && discarded != 0) ||
234                   (n == 5 && discarded == 0 && ((lsb / lsbthreshold) & 1) == 1);  // round half-even
235             break;
236 
237         case ROUND_HALF_ODD:
238             inc = (n > 5) ||
239                   (n == 5 && discarded != 0) ||
240                   (n == 5 && discarded == 0 && ((lsb / lsbthreshold) & 1) == 0);  // round half-odd
241             break;
242 
243         case ROUND_CEIL:
244             inc = (sign == 1) && (n != 0 || discarded != 0);  // round ceil
245             break;
246 
247         case ROUND_FLOOR:
248         default:
249             inc = (sign == -1) && (n != 0 || discarded != 0);  // round floor
250             break;
251         }
252 
253         if (inc) {
254             // increment if necessary
255             int rh = lsbthreshold;
256             for (int i = lsd; i < mant.length; i++) {
257                 final int r = mant[i] + rh;
258                 rh = r / RADIX;
259                 mant[i] = r % RADIX;
260             }
261 
262             if (rh != 0) {
263                 shiftRight();
264                 mant[mant.length - 1] = rh;
265             }
266         }
267 
268         // Check for exceptional cases and raise signals if necessary
269         if (exp < MIN_EXP) {
270             // Gradual Underflow
271             getField().setIEEEFlagsBits(DfpField.FLAG_UNDERFLOW);
272             return DfpField.FLAG_UNDERFLOW;
273         }
274 
275         if (exp > MAX_EXP) {
276             // Overflow
277             getField().setIEEEFlagsBits(DfpField.FLAG_OVERFLOW);
278             return DfpField.FLAG_OVERFLOW;
279         }
280 
281         if (n != 0 || discarded != 0) {
282             // Inexact
283             getField().setIEEEFlagsBits(DfpField.FLAG_INEXACT);
284             return DfpField.FLAG_INEXACT;
285         }
286         return 0;
287     }
288 
289     /** {@inheritDoc} */
290     @Override
291     public Dfp nextAfter(Dfp x) {
292 
293         final String trapName = "nextAfter";
294 
295         // make sure we don't mix number with different precision
296         if (getField().getRadixDigits() != x.getField().getRadixDigits()) {
297             getField().setIEEEFlagsBits(DfpField.FLAG_INVALID);
298             final Dfp result = newInstance(getZero());
299             result.nans = QNAN;
300             return dotrap(DfpField.FLAG_INVALID, trapName, x, result);
301         }
302 
303         boolean up = false;
304         Dfp result;
305         Dfp inc;
306 
307         // if this is greater than x
308         if (this.lessThan(x)) {
309             up = true;
310         }
311 
312         if (equals(x)) {
313             return newInstance(x);
314         }
315 
316         if (lessThan(getZero())) {
317             up = !up;
318         }
319 
320         if (up) {
321             inc = power10(intLog10() - getDecimalDigits() + 1);
322             inc = copySign(inc, this);
323 
324             if (this.equals(getZero())) {
325                 inc = power10K(MIN_EXP - mant.length - 1);
326             }
327 
328             if (inc.equals(getZero())) {
329                 result = copySign(newInstance(getZero()), this);
330             } else {
331                 result = add(inc);
332             }
333         } else {
334             inc = power10(intLog10());
335             inc = copySign(inc, this);
336 
337             if (this.equals(inc)) {
338                 inc = inc.divide(power10(getDecimalDigits()));
339             } else {
340                 inc = inc.divide(power10(getDecimalDigits() - 1));
341             }
342 
343             if (this.equals(getZero())) {
344                 inc = power10K(MIN_EXP - mant.length - 1);
345             }
346 
347             if (inc.equals(getZero())) {
348                 result = copySign(newInstance(getZero()), this);
349             } else {
350                 result = subtract(inc);
351             }
352         }
353 
354         if (result.classify() == INFINITE && this.classify() != INFINITE) {
355             getField().setIEEEFlagsBits(DfpField.FLAG_INEXACT);
356             result = dotrap(DfpField.FLAG_INEXACT, trapName, x, result);
357         }
358 
359         if (result.equals(getZero()) && !this.equals(getZero())) {
360             getField().setIEEEFlagsBits(DfpField.FLAG_INEXACT);
361             result = dotrap(DfpField.FLAG_INEXACT, trapName, x, result);
362         }
363 
364         return result;
365     }
366 }