001: // Copyright (c) 1999 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.math;
005:
006: import java.text.FieldPosition;
007:
008: /** Format a real number using a floating-point format.
009: * However, if `general' is true, and the number "fits",
010: * use a fixed-point format (like printf %g).
011: * Used for Common Lisp specs ~E and ~G; also C-style %e and %g.
012: */
013:
014: public class ExponentialFormat extends java.text.Format {
015: /** Number of fractional digits to show.
016: * This is `d' in the CommonLisp spec. */
017: public int fracDigits = -1;
018:
019: /** Number of digits to show in the integer part of the result.
020: * If positive, The number of digits before the decimal point.
021: * If negative, the -intDigits zeros are emitted after the decimal point.
022: * This is `k' in the CommonLisp spec. */
023: public int intDigits;
024:
025: /** Number of digits to show in the exponent.
026: * Zero means unspecified - show as many as needed. */
027: public int expDigits;
028:
029: public char overflowChar;
030: public char padChar;
031: public char exponentChar = 'E';
032: /** Display sign of exponent even when it is non-negative. */
033: public boolean exponentShowSign;
034:
035: /** True if '+' should be printed for non-negative number. */
036: public boolean showPlus;
037:
038: public int width;
039: public boolean general;
040:
041: static final double LOG10 = Math.log(10);
042:
043: /** Add 1 to the integer in sbuf from digStart to digEnd.
044: * @return if we overflowed. */
045: static boolean addOne(StringBuffer sbuf, int digStart, int digEnd) {
046: for (int j = digEnd;;) {
047: if (j == digStart) {
048: sbuf.insert(j, '1');
049: return true;
050: }
051: char ch = sbuf.charAt(--j);
052: if (ch != '9') {
053: sbuf.setCharAt(j, (char) ((int) ch + 1));
054: return false;
055: }
056: sbuf.setCharAt(j, '0');
057: }
058: }
059:
060: public StringBuffer format(float value, StringBuffer sbuf,
061: FieldPosition fpos) {
062: return format(value, fracDigits < 0 ? Float.toString(value)
063: : null, sbuf, fpos);
064: }
065:
066: public StringBuffer format(double value, StringBuffer sbuf,
067: FieldPosition fpos) {
068: return format(value, fracDigits < 0 ? Double.toString(value)
069: : null, sbuf, fpos);
070: }
071:
072: StringBuffer format(double value, String dstr, StringBuffer sbuf,
073: FieldPosition fpos) {
074: int k = intDigits;
075: int d = fracDigits;
076: boolean negative = value < 0;
077: if (negative)
078: value = -value;
079: int oldLen = sbuf.length();
080: int signLen = 1;
081: if (negative) {
082: if (d >= 0)
083: sbuf.append('-');
084: // Otherwise emitted by RealNum.toStringScientific.
085: } else if (showPlus)
086: sbuf.append('+');
087: else
088: signLen = 0;
089: // Number of significant digits.
090: int digits, scale;
091: int digStart = sbuf.length();
092: int exponent;
093: boolean nonFinite = Double.isNaN(value)
094: || Double.isInfinite(value);
095: if (d < 0 || nonFinite) {
096: if (dstr == null)
097: dstr = Double.toString(value); // Needed if nonFinite && d >= 0.
098: int indexE = dstr.indexOf('E');
099: if (indexE >= 0) {
100: sbuf.append(dstr);
101: indexE += digStart;
102: boolean negexp = dstr.charAt(indexE + 1) == '-';
103: exponent = 0;
104: for (int i = indexE + (negexp ? 2 : 1); i < sbuf
105: .length(); i++)
106: exponent = 10 * exponent + (sbuf.charAt(i) - '0');
107: if (negexp)
108: exponent = -exponent;
109: sbuf.setLength(indexE);
110: } else
111: exponent = RealNum.toStringScientific(dstr, sbuf);
112: if (negative)
113: digStart++;
114: int dot = digStart + 1;
115: /* #ifdef JAVA2 */
116: sbuf.deleteCharAt(dot);
117: /* #else */
118: // String afterDot = sbuf.toString().substring(dot+1);
119: // sbuf.setLength(dot);
120: // sbuf.append(afterDot);
121: /* #endif */
122: digits = sbuf.length() - digStart;
123: // Remove trailing '0' added by RealNum.toStringScientific.
124: if (digits > 1 && sbuf.charAt(digStart + digits - 1) == '0')
125: sbuf.setLength(digStart + --digits);
126: scale = digits - exponent - 1;
127: } else {
128: digits = d + (k > 0 ? 1 : k);
129: int log = (int) (Math.log(value) / LOG10 + 1000.0); // floor
130: if (log == 0x80000000) // value is zero
131: log = 0;
132: else
133: log = log - 1000;
134: scale = digits - log - 1;
135: RealNum.toScaledInt(value, scale).format(10, sbuf);
136: exponent = digits - 1 - scale;
137: }
138:
139: exponent -= k - 1;
140: int exponentAbs = exponent < 0 ? -exponent : exponent;
141: int exponentLen = exponentAbs >= 1000 ? 4
142: : exponentAbs >= 100 ? 3 : exponentAbs >= 10 ? 2 : 1;
143: if (expDigits > exponentLen)
144: exponentLen = expDigits;
145: boolean showExponent = true;
146: int ee = !general ? 0 : expDigits > 0 ? expDigits + 2 : 4;
147: boolean fracUnspecified = d < 0;
148: if (general || fracUnspecified) {
149: int n = digits - scale;
150: if (fracUnspecified) {
151: d = n < 7 ? n : 7;
152: if (digits > d)
153: d = digits;
154: }
155: int dd = d - n;
156: if (general && (n >= 0 && dd >= 0)) {
157: // "arg is printed as if by the format directives
158: // ~ww,dd,0,overflowchar,padcharF~ee@T "
159: digits = d;
160: k = n;
161: showExponent = false;
162: } else if (fracUnspecified) {
163: if (width <= 0)
164: digits = d;
165: else {
166: int avail = width - signLen - exponentLen - 3;
167: digits = avail;
168: if (k < 0)
169: digits -= k;
170: if (digits > d)
171: digits = d;
172: }
173: if (digits <= 0)
174: digits = 1;
175: }
176: }
177:
178: int digEnd = digStart + digits;
179: while (sbuf.length() < digEnd)
180: sbuf.append('0');
181:
182: // Now round to specified digits.
183: char nextDigit = digEnd == sbuf.length() ? '0' : sbuf
184: .charAt(digEnd);
185: boolean addOne = nextDigit >= '5';
186: // || (nextDigit == '5'
187: // && (Character.digit(sbuf.charAt(digEnd-1), 10) & 1) == 0);
188: if (addOne && addOne(sbuf, digStart, digEnd))
189: scale++;
190: // Truncate excess digits, after adjusting scale accordingly.
191: scale -= sbuf.length() - digEnd;
192: sbuf.setLength(digEnd);
193:
194: int dot = digStart;
195: if (k < 0) {
196: // Insert extra zeros after '.'.
197: for (int j = k; ++j <= 0;)
198: sbuf.insert(digStart, '0');
199: } else {
200: // Insert extra zeros before '.', if needed.
201: for (; digStart + k > digEnd; digEnd++)
202: sbuf.append('0');
203: dot += k;
204: }
205: if (nonFinite)
206: showExponent = false;
207: else
208: sbuf.insert(dot, '.');
209:
210: int newLen, i;
211: if (showExponent) {
212: // Append the exponent.
213: sbuf.append(exponentChar);
214: if (exponentShowSign || exponent < 0)
215: sbuf.append(exponent >= 0 ? '+' : '-');
216: i = sbuf.length();
217: sbuf.append(exponentAbs);
218: newLen = sbuf.length();
219: int j = expDigits - (newLen - i);
220: if (j > 0) { // Insert extra exponent digits.
221: newLen += j;
222: while (--j >= 0)
223: sbuf.insert(i, '0');
224: }
225: } else {
226: exponentLen = 0;
227: }
228: newLen = sbuf.length();
229: int used = newLen - oldLen;
230: i = width - used;
231:
232: // Insert '0' after '.' if needed and there is space.
233: if (fracUnspecified
234: && (dot + 1 == sbuf.length() || sbuf.charAt(dot + 1) == exponentChar)
235: && (width <= 0 || i > 0)) {
236: i--;
237: sbuf.insert(dot + 1, '0');
238: }
239:
240: if ((i >= 0 || width <= 0)
241: && !(showExponent && exponentLen > expDigits
242: && expDigits > 0 && overflowChar != '\0')) {
243: // Insert optional '0' before '.' if there is space.
244: if (k <= 0 && (i > 0 || width <= 0)) {
245: sbuf.insert(digStart, '0');
246: --i;
247: }
248: if (!showExponent
249: // The CommonLisp spec requires adding spaces on the right
250: // when a ~g format ends up using fixed-point format, corresponding
251: // to the space otherwise used for the exponent. However, it seems
252: // wrong to do so when using a variable-width format.
253: && width > 0) {
254: for (; --ee >= 0; --i)
255: // && sbuf.length() < oldLen + width; --i)
256: sbuf.append(' ');
257: }
258: // Insert padding:
259: while (--i >= 0)
260: sbuf.insert(oldLen, padChar);
261: } else if (overflowChar != '\0') {
262: sbuf.setLength(oldLen);
263: for (i = width; --i >= 0;)
264: sbuf.append(overflowChar);
265: }
266: return sbuf;
267: }
268:
269: public StringBuffer format(long num, StringBuffer sbuf,
270: FieldPosition fpos) {
271: return format((double) num, sbuf, fpos);
272: }
273:
274: public StringBuffer format(Object num, StringBuffer sbuf,
275: FieldPosition fpos) {
276: // Common Lisp says if value is non-real, print as if with ~wD. FIXME.
277: return format(((RealNum) num).doubleValue(), sbuf, fpos);
278: }
279:
280: public java.lang.Number parse(String text,
281: java.text.ParsePosition status) {
282: throw new Error("ExponentialFormat.parse - not implemented");
283: }
284:
285: public Object parseObject(String text,
286: java.text.ParsePosition status) {
287: throw new Error(
288: "ExponentialFormat.parseObject - not implemented");
289: }
290:
291: }
|