001/**
002 * RoundingStrategy.java
003 *
004 * Copyright (c) 2004-2012, Nicole C. Tedesco.  All rights reserved.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at:
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package net.sf.jaccumulator.scalars;
020
021import java.math.*;
022import java.util.EnumMap;
023import java.util.Map;
024
025// TABLE OF CONTENTS
026// =================
027// Select the entire text line of the subject you want to jump to (including the
028// leading slashes), then execute a Find on that topic (usually Ctrl+F). In
029// most editors, the Find function will automatically replace the "find" topic
030// with your highlighted text. If not then you may need to copy then paste your
031// topic into the appropriate field.
032//
033// * Initialization and Finalization
034// * Rounding Modes
035// * Factories
036// * Conversions
037//
038// * Ceiling
039// * Down
040// * Floor
041// * Half Down
042// * Half Even
043// * Half Up
044// * Up
045// * Unnecessary
046//
047
048/**
049 * Strategies for floating point-to-integral conversions
050 *
051 * @since JAccumulator 4.0
052 * @author Nicole Tedesco (<a
053 *         href="mailto:Nicole@NicoleTedesco.com">Nicole@NicoleTedesco.com</a>)
054 */
055public abstract class RoundingStrategy
056{
057    private final static Map<RoundingMode,RoundingStrategy> _theStrategies = new EnumMap<RoundingMode,RoundingStrategy>(
058        RoundingMode.class);
059
060    protected static final int CACHE_SIZE = 10;
061
062    /**
063     * @see Math#ceil(double)
064     * @see RoundingMode#CEILING
065     */
066    public final static RoundingStrategy CEILING = new Ceiling();
067
068    /**
069     * @see RoundingMode#DOWN
070     */
071    public final static RoundingStrategy DOWN = new Down();
072
073    /**
074     * @see Math#floor(double)
075     * @see RoundingMode#FLOOR
076     */
077    public final static RoundingStrategy FLOOR = new Floor();
078
079    /**
080     * @see RoundingMode#HALF_DOWN
081     */
082    public final static RoundingStrategy HALF_DOWN = new HalfDown();
083
084    /**
085     * @see Math#rint(double)
086     * @see RoundingMode#HALF_EVEN
087     */
088    public final static RoundingStrategy HALF_EVEN = new HalfEven();
089
090    /**
091     * @see RoundingMode#HALF_UP
092     */
093    public final static RoundingStrategy HALF_UP = new HalfUp();
094
095    /**
096     * @see RoundingMode#UNNECESSARY
097     */
098    public final static RoundingStrategy UNNECESSARY = new Unnecessary();
099
100    /**
101     * @see RoundingMode#UP
102     */
103    public final static RoundingStrategy UP = new Up();
104
105    static {
106        _theStrategies.put(CEILING.getRoundingMode(), CEILING);
107        _theStrategies.put(DOWN.getRoundingMode(), DOWN);
108        _theStrategies.put(FLOOR.getRoundingMode(), FLOOR);
109        _theStrategies.put(HALF_DOWN.getRoundingMode(), HALF_DOWN);
110        _theStrategies.put(HALF_EVEN.getRoundingMode(), HALF_EVEN);
111        _theStrategies.put(HALF_UP.getRoundingMode(), HALF_UP);
112        _theStrategies.put(UP.getRoundingMode(), UP);
113        _theStrategies.put(UNNECESSARY.getRoundingMode(), UNNECESSARY);
114    }
115    protected final MathContext[] _theContextCache;
116
117    // ----------
118    // * Initialization and Finalization
119    //
120
121    protected RoundingStrategy()
122    {
123        final RoundingMode aMode = this.getRoundingMode();
124        this._theContextCache = new MathContext[CACHE_SIZE];
125        for (int i = 0; i < CACHE_SIZE; i++) {
126            this._theContextCache[i] = new MathContext(i, aMode);
127        }
128    }
129
130    // ----------
131    // * Rounding Modes
132    //
133
134    public abstract int getBigDecimalRoundingMode();
135
136    public MathContext getMathContext( final int precision ) {
137        if ((0 <= precision) && (precision < CACHE_SIZE)) {
138            return this._theContextCache[precision];
139        }
140        return new MathContext(precision, this.getRoundingMode());
141    }
142
143    // ----------
144    // * Conversions
145    //
146
147    public abstract RoundingMode getRoundingMode();
148
149    public final boolean booleanValue( final double aNumber ) {
150        return this.longValue(aNumber) != 0L;
151    }
152
153    public final boolean booleanValue( final float aNumber ) {
154        return this.longValue(aNumber) != 0L;
155    }
156
157    public final byte byteValue( final double aNumber ) {
158        return (byte)this.intValue(aNumber);
159    }
160
161    public final byte byteValue( final float aNumber ) {
162        return (byte)this.intValue(aNumber);
163    }
164
165    public final char charValue( final double aNumber ) {
166        return (char)this.intValue(aNumber);
167    }
168
169    public final char charValue( final float aNumber ) {
170        return (char)this.intValue(aNumber);
171    }
172
173    public abstract double doubleValue( final double aNumber );
174
175    public final float floatValue( final float aNumber ) {
176        return (float)this.doubleValue(aNumber);
177    }
178
179    public abstract int intValue( final double aNumber );
180
181    public abstract int intValue( final float aNumber );
182
183    public abstract long longValue( final double aNumber );
184
185    public abstract long longValue( final float aNumber );
186
187    public final short shortValue( final double aNumber ) {
188        return (short)this.intValue(aNumber);
189    }
190
191    public final short shortValue( final float aNumber ) {
192        return (short)this.intValue(aNumber);
193    }
194
195    public final BigDecimal toUnlimitedDecimal( final BigDecimal aNumber ) {
196        final RoundingMode aRoundingMode = this.getRoundingMode();
197        final BigDecimal bdNew = aNumber.setScale(0, aRoundingMode);
198        return bdNew;
199    }
200
201    public final BigInteger toUnlimitedInteger( final double aNumber ) {
202        final BigDecimal bd = toUnlimitedDecimal(aNumber);
203        final BigDecimal rounded = this.toUnlimitedDecimal(bd);
204        final BigInteger bi = rounded.toBigInteger();
205        return bi;
206    }
207
208    // ----------
209    // * Factories
210    //
211
212    public final BigInteger toUnlimitedInteger( final float aNumber ) {
213        final BigDecimal bd = toUnlimitedDecimal(aNumber);
214        final BigDecimal rounded = this.toUnlimitedDecimal(bd);
215        final BigInteger bi = rounded.toBigInteger();
216        return bi;
217    }
218
219    public final static RoundingStrategy getInstance(
220        final int aBigDecimalRoundingMode )
221    {
222        return getInstance(RoundingMode.valueOf(aBigDecimalRoundingMode));
223    }
224
225    public final static RoundingStrategy getInstance( final RoundingMode aMode )
226    {
227        return _theStrategies.get(aMode);
228    }
229
230    // ----------
231    // * Ceiling
232    //
233
234    private static class Ceiling
235        extends
236            RoundingStrategy
237    {
238        public Ceiling()
239        {
240        }
241
242        @Override
243        public final int getBigDecimalRoundingMode() {
244            return BigDecimal.ROUND_CEILING;
245        }
246
247        @Override
248        public final RoundingMode getRoundingMode() {
249            return RoundingMode.CEILING;
250        }
251
252        @Override
253        public final double doubleValue( final double aNumber ) {
254            return Math.ceil(aNumber);
255        }
256
257        @Override
258        public final int intValue( final double aNumber ) {
259            return (int)this.doubleValue(aNumber);
260        }
261
262        @Override
263        public final int intValue( final float aNumber ) {
264            return (int)this.doubleValue(aNumber);
265        }
266
267        @Override
268        public final long longValue( final double aNumber ) {
269            return (long)this.doubleValue(aNumber);
270        }
271
272        @Override
273        public final long longValue( final float aNumber ) {
274            return (long)this.doubleValue(aNumber);
275        }
276    }
277
278    // ----------
279    // * Down
280    //
281
282    private static class Down
283        extends
284            RoundingStrategy
285    {
286        public Down()
287        {
288        }
289
290        @Override
291        public final int getBigDecimalRoundingMode() {
292            return BigDecimal.ROUND_DOWN;
293        }
294
295        @Override
296        public final RoundingMode getRoundingMode() {
297            return RoundingMode.DOWN;
298        }
299
300        @Override
301        public final double doubleValue( final double aNumber ) {
302            return (long)aNumber;
303        }
304
305        @Override
306        public final int intValue( final double aNumber ) {
307            return (int)aNumber;
308        }
309
310        @Override
311        public final int intValue( final float aNumber ) {
312            return (int)aNumber;
313        }
314
315        @Override
316        public final long longValue( final double aNumber ) {
317            return (long)aNumber;
318        }
319
320        @Override
321        public final long longValue( final float aNumber ) {
322            return (long)aNumber;
323        }
324    }
325
326    // ----------
327    // * Floor
328    //
329
330    private static class Floor
331        extends
332            RoundingStrategy
333    {
334        public Floor()
335        {
336        }
337
338        @Override
339        public final int getBigDecimalRoundingMode() {
340            return BigDecimal.ROUND_FLOOR;
341        }
342
343        @Override
344        public final RoundingMode getRoundingMode() {
345            return RoundingMode.FLOOR;
346        }
347
348        @Override
349        public final double doubleValue( final double aNumber ) {
350            return Math.floor(aNumber);
351        }
352
353        @Override
354        public final int intValue( final double aNumber ) {
355            return (int)this.doubleValue(aNumber);
356        }
357
358        @Override
359        public final int intValue( final float aNumber ) {
360            return (int)this.doubleValue(aNumber);
361        }
362
363        @Override
364        public final long longValue( final double aNumber ) {
365            return (long)this.doubleValue(aNumber);
366        }
367
368        @Override
369        public final long longValue( final float aNumber ) {
370            return (long)this.doubleValue(aNumber);
371        }
372    }
373
374    // ----------
375    // * Half Down
376    //
377
378    private static class HalfDown
379        extends
380            RoundingStrategy
381    {
382        public HalfDown()
383        {
384        }
385
386        @Override
387        public final int getBigDecimalRoundingMode() {
388            return BigDecimal.ROUND_HALF_DOWN;
389        }
390
391        @Override
392        public final RoundingMode getRoundingMode() {
393            return RoundingMode.HALF_DOWN;
394        }
395
396        @Override
397        public final double doubleValue( final double aNumber ) {
398            if (aNumber < 0.0D) {
399                if (DOWN.doubleValue(aNumber) == (aNumber + 0.5D)) {
400                    return DOWN.doubleValue(aNumber);
401                }
402                return (long)(aNumber - 0.5D);
403            }
404            if (DOWN.doubleValue(aNumber) == (aNumber - 0.5D)) {
405                return DOWN.doubleValue(aNumber);
406            }
407            return (long)(aNumber + 0.5D);
408        }
409
410        @Override
411        public final int intValue( final double aNumber ) {
412            return (int)this.longValue(aNumber);
413        }
414
415        @Override
416        public final int intValue( final float aNumber ) {
417            return (int)this.longValue(aNumber);
418        }
419
420        @Override
421        public final long longValue( final double aNumber ) {
422            if (aNumber < 0.0D) {
423                if (DOWN.doubleValue(aNumber) == (aNumber + 0.5D)) {
424                    return DOWN.longValue(aNumber);
425                }
426                return (long)(aNumber - 0.5D);
427            }
428            if (DOWN.doubleValue(aNumber) == (aNumber - 0.5D)) {
429                return DOWN.longValue(aNumber);
430            }
431            return (long)(aNumber + 0.5D);
432        }
433
434        @Override
435        public final long longValue( final float aNumber ) {
436            if (aNumber < 0.0D) {
437                if (DOWN.doubleValue(aNumber) == (aNumber + 0.5D)) {
438                    return DOWN.longValue(aNumber);
439                }
440                return (long)(aNumber - 0.5D);
441            }
442            if (DOWN.doubleValue(aNumber) == (aNumber - 0.5D)) {
443                return DOWN.longValue(aNumber);
444            }
445            return (long)(aNumber + 0.5D);
446        }
447    }
448
449    // ----------
450    // * Half Even
451    //
452
453    private static class HalfEven
454        extends
455            RoundingStrategy
456    {
457        public HalfEven()
458        {
459        }
460
461        @Override
462        public final int getBigDecimalRoundingMode() {
463            return BigDecimal.ROUND_HALF_EVEN;
464        }
465
466        @Override
467        public final RoundingMode getRoundingMode() {
468            return RoundingMode.HALF_EVEN;
469        }
470
471        @Override
472        public final double doubleValue( final double aNumber ) {
473            return Math.rint(aNumber);
474        }
475
476        @Override
477        public final int intValue( final double aNumber ) {
478            return (int)this.doubleValue(aNumber);
479        }
480
481        @Override
482        public final int intValue( final float aNumber ) {
483            return (int)this.doubleValue(aNumber);
484        }
485
486        @Override
487        public final long longValue( final double aNumber ) {
488            return (long)this.doubleValue(aNumber);
489        }
490
491        @Override
492        public final long longValue( final float aNumber ) {
493            return (long)this.doubleValue(aNumber);
494        }
495    }
496
497    // ----------
498    // * Half Up
499    //
500
501    private static class HalfUp
502        extends
503            RoundingStrategy
504    {
505        public HalfUp()
506        {
507        }
508
509        @Override
510        public final int getBigDecimalRoundingMode() {
511            return BigDecimal.ROUND_HALF_UP;
512        }
513
514        @Override
515        public final RoundingMode getRoundingMode() {
516            return RoundingMode.HALF_UP;
517        }
518
519        @Override
520        public final double doubleValue( final double aNumber ) {
521            return this.longValue(aNumber);
522        }
523
524        @Override
525        public final int intValue( final double aNumber ) {
526            if (aNumber < 0.0D) {
527                return (int)(aNumber - 0.5D);
528            }
529            return (int)(aNumber + 0.5D);
530        }
531
532        @Override
533        public final int intValue( final float aNumber ) {
534            if (aNumber < 0.0F) {
535                return (int)(aNumber - 0.5F);
536            }
537            return (int)(aNumber + 0.5F);
538        }
539
540        @Override
541        public final long longValue( final double aNumber ) {
542            if (aNumber < 0.0D) {
543                return (long)(aNumber - 0.5D);
544            }
545            return (long)(aNumber + 0.5D);
546        }
547
548        @Override
549        public final long longValue( final float aNumber ) {
550            if (aNumber < 0.0F) {
551                return (long)(aNumber - 0.5F);
552            }
553            return (long)(aNumber + 0.5F);
554        }
555    }
556
557    // ----------
558    // * Up
559    //
560
561    private static class Unnecessary
562        extends
563            RoundingStrategy
564    {
565        public Unnecessary()
566        {
567        }
568
569        @Override
570        public final int getBigDecimalRoundingMode() {
571            return BigDecimal.ROUND_UNNECESSARY;
572        }
573
574        @Override
575        public final RoundingMode getRoundingMode() {
576            return RoundingMode.UNNECESSARY;
577        }
578
579        @Override
580        public final double doubleValue( final double aNumber ) {
581            if (DOWN.doubleValue(aNumber) == aNumber) {
582                return aNumber;
583            }
584            throw new ArithmeticException();
585        }
586
587        @Override
588        public final int intValue( final double aNumber ) {
589            if (DOWN.doubleValue(aNumber) == aNumber) {
590                return DOWN.intValue(aNumber);
591            }
592            throw new ArithmeticException();
593        }
594
595        @Override
596        public final int intValue( final float aNumber ) {
597            if (DOWN.doubleValue(aNumber) == aNumber) {
598                return DOWN.intValue(aNumber);
599            }
600            throw new ArithmeticException();
601        }
602
603        @Override
604        public final long longValue( final double aNumber ) {
605            if (DOWN.doubleValue(aNumber) == aNumber) {
606                return DOWN.longValue(aNumber);
607            }
608            throw new ArithmeticException();
609        }
610
611        @Override
612        public final long longValue( final float aNumber ) {
613            if (DOWN.doubleValue(aNumber) == aNumber) {
614                return DOWN.longValue(aNumber);
615            }
616            throw new ArithmeticException();
617        }
618    }
619
620    // ----------
621    // * Unnecessary
622    //
623
624    private static class Up
625        extends
626            RoundingStrategy
627    {
628        public Up()
629        {
630        }
631
632        @Override
633        public final int getBigDecimalRoundingMode() {
634            return BigDecimal.ROUND_UP;
635        }
636
637        @Override
638        public final RoundingMode getRoundingMode() {
639            return RoundingMode.UP;
640        }
641
642        @Override
643        public final double doubleValue( final double aNumber ) {
644            if (DOWN.doubleValue(aNumber) == aNumber) {
645                return DOWN.doubleValue(aNumber);
646            }
647            if (aNumber < 0.0D) {
648                return DOWN.doubleValue(aNumber - 1.0D);
649            }
650            return DOWN.doubleValue(aNumber + 1.0D);
651        }
652
653        @Override
654        public final int intValue( final double aNumber ) {
655            return (int)this.doubleValue(aNumber);
656        }
657
658        @Override
659        public final int intValue( final float aNumber ) {
660            return (int)this.doubleValue(aNumber);
661        }
662
663        @Override
664        public final long longValue( final double aNumber ) {
665            return (long)this.doubleValue(aNumber);
666        }
667
668        @Override
669        public final long longValue( final float aNumber ) {
670            return (long)this.doubleValue(aNumber);
671        }
672    }
673
674    public static final BigDecimal toUnlimitedDecimal( final double aValue ) {
675        return toUnlimitedDecimal(String.valueOf(aValue));
676    }
677
678    public static final BigDecimal toUnlimitedDecimal( final float aValue ) {
679        return toUnlimitedDecimal(String.valueOf(aValue));
680    }
681
682    public static final BigDecimal toUnlimitedDecimal( final String aValue ) {
683        try {
684            final BigDecimal ud = new BigDecimal(aValue);
685            return ud;
686        } catch (final NumberFormatException ex) {
687        }
688        return BigDecimal.ZERO;
689    }
690}