001/*
002// This software is subject to the terms of the Eclipse Public License v1.0
003// Agreement, available at the following URL:
004// http://www.eclipse.org/legal/epl-v10.html.
005// You must accept the terms of that agreement to use this software.
006//
007// Copyright (C) 2012-2012 Pentaho
008// All Rights Reserved.
009 */
010package mondrian.util;
011
012/**
013 * Representation of a number as a list of digits.
014 *
015 * @author tkafalas
016 */
017class MondrianFloatingDecimal {
018    boolean isExceptional;
019    boolean isNegative;
020    int decExponent;
021    char digits[];
022    int nDigits;
023    private final DigitList digitList = new DigitList();
024    private final DigitList expDigitList = new DigitList();
025
026    private static final int MAX_SIGNIFICANT_DIGITS = 19;
027
028    public MondrianFloatingDecimal(double d) {
029        if (d < 0) {
030            isNegative = true;
031            d = -d;
032        } else {
033            isNegative = false;
034        }
035        digitList.set(d, MAX_SIGNIFICANT_DIGITS, true);
036        nDigits = 0;
037        for (int i = 0; i < digitList.digits.length; i++) {
038            if (digitList.digits[i] != 0) {
039                nDigits++;
040            }
041        }
042        digits = toCharArray(digitList.digits);
043        isExceptional = Double.isInfinite(d);
044        decExponent = digitList.decimalAt;
045    }
046
047    public String toString() {
048        final StringBuilder s = new StringBuilder(MAX_SIGNIFICANT_DIGITS);
049        if (nDigits == 0) {
050            return "0";
051        }
052        if (isNegative) {
053            s.append("-");
054        }
055        if (decExponent < -5) {
056            s.append('.').append(digits).append("E-").append(decExponent);
057        } else {
058            if (decExponent < 0) {
059                s.append('.');
060                for (int i = 0; i < decExponent; i++) {
061                    s.append('0');
062                }
063                s.append(digits);
064            } else {
065                if (decExponent < nDigits) {
066                    s.append(digits, 0, decExponent).append(".")
067                            .append(digits, decExponent, nDigits - decExponent);
068                } else {
069                    if (decExponent == nDigits) {
070                        s.append(digits);
071                    } else {
072                        if (decExponent < nDigits + 10) {
073                            s.append(digits);
074                            for (int i = 0; i < decExponent - nDigits; i++) {
075                                s.append('0');
076                            }
077                        } else {
078                            s.append('.').append(digits).append("E")
079                                .append(decExponent);
080                        }
081                    }
082                }
083            }
084        }
085        return s.toString();
086    }
087
088    /**
089     * Appends {@link #decExponent} to result string. Returns i plus the
090     * number of chars written.
091     *
092     * <p>Implementation may assume that exponent has 3 or fewer digits.</p>
093     *
094     * <p>For example, given {@code decExponent} = 2,
095     * {@code formatExponent(result, 5, true, 2)}
096     * will write '0' into result[5]
097     * and '2' into result[6] and return 7.</p>
098     *
099     * @param result Result buffer
100     * @param i Initial offset into result buffer
101     * @param expSign Whether to print a '+' sign if exponent is positive
102     *                (always prints '-' if negative)
103     * @param minExpDigits Minimum number of digits to write
104     * @return Offset into result buffer after writing chars
105     */
106    public int formatExponent(
107        char[] result,
108        int i,
109        boolean expSign,
110        int minExpDigits)
111    {
112        int useExp = nDigits == 0 ? 0 : decExponent - 1;
113        expDigitList.set(Math.abs(useExp));
114        if (useExp < 0 || expSign) {
115            result[i++] = useExp < 0 ? '-' : '+';
116        }
117        if (minExpDigits > expDigitList.decimalAt) {
118            for (int j = 0; j < minExpDigits - expDigitList.decimalAt; j++) {
119                result[i++] = '0';
120            }
121        }
122        for (int j = 0; j < expDigitList.decimalAt; j++) {
123            result[i++] = ((char) expDigitList.digits[j]);
124        }
125        return i;
126    }
127
128    private char[] toCharArray(byte[] bytes) {
129        char[] chars = new char[bytes.length];
130        for (int i = 0; i < bytes.length; i++) {
131            chars[i] = (char) bytes[i];
132        }
133        return chars;
134    }
135}
136
137// End MondrianFloatingDecimal.java