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: // Can't user NumberFormat, because it's format(Object, StringBuffer,
009: // FieldPosition) method is final - and does the wrong thing.
010: // (It ends up converting gnu.math number types to long!)
011:
012: /** Format a real number using a fixed-point format.
013: * Used for Common Lisp specs ~F and ~$; also C-style %f.
014: */
015:
016: public class FixedRealFormat extends java.text.Format {
017: private int i, d;
018:
019: public int getMaximumFractionDigits() {
020: return d;
021: }
022:
023: public int getMinimumIntegerDigits() {
024: return i;
025: }
026:
027: public void setMaximumFractionDigits(int d) {
028: this .d = d;
029: }
030:
031: public void setMinimumIntegerDigits(int i) {
032: this .i = i;
033: }
034:
035: // These should not be public. FIXME.
036: public int width;
037: public int scale;
038: public char padChar;
039: public boolean showPlus;
040: public boolean internalPad;
041: public char overflowChar;
042:
043: public void format(RatNum number, boolean negative,
044: StringBuffer sbuf, FieldPosition fpos) {
045: int decimals = getMaximumFractionDigits();
046: int digits;
047: int oldSize = sbuf.length();
048: int signLen = 1;
049: if (negative)
050: sbuf.append('-');
051: else if (showPlus)
052: sbuf.append('+');
053: else
054: signLen = 0;
055: String string;
056: int length;
057: if (decimals < 0) {
058: double val = number.doubleValue();
059: int log = (int) (Math.log(val) / ExponentialFormat.LOG10);
060: int cur_scale = log == 0x80000000 ? 0 : 17 - log;
061: string = RealNum.toScaledInt(val, cur_scale).toString();
062: int i = string.length();
063: digits = i - cur_scale + scale;
064: if (width > 0)
065: decimals = width - signLen - 1 - digits;
066: else
067: decimals = (i > 16 ? 16 : i) - digits;
068: if (decimals < 0)
069: decimals = 0;
070: sbuf.append(string);
071: int digStart = oldSize + signLen;
072: int digEnd = digStart + digits + decimals;
073: i = sbuf.length();
074: char nextDigit;
075: if (digEnd >= i) {
076: digEnd = i;
077: nextDigit = '0';
078: } else
079: nextDigit = sbuf.charAt(digEnd);
080: boolean addOne = nextDigit >= '5';
081: char skip = addOne ? '9' : '0';
082: while (digEnd > digStart + digits
083: && sbuf.charAt(digEnd - 1) == skip)
084: digEnd--;
085: length = digEnd - digStart;
086: decimals = length - digits;
087: if (addOne) {
088: if (ExponentialFormat.addOne(sbuf, digStart, digEnd)) {
089: digits++;
090: decimals = 0;
091: length = digits;
092: }
093: }
094: if (decimals == 0
095: && (width <= 0 || signLen + digits + 1 < width)) {
096: decimals = 1;
097: length++;
098: // This is only needed if number==0.0:
099: sbuf.insert(digStart + digits, '0');
100: }
101: sbuf.setLength(digStart + length);
102: } else {
103: string = RealNum.toScaledInt(number, decimals + scale)
104: .toString();
105: sbuf.append(string);
106: length = string.length();
107: digits = length - decimals;
108: }
109:
110: int total_digits = digits + decimals;
111: // Number of initial zeros to add.
112: int zero_digits = getMinimumIntegerDigits();
113: if (digits >= 0 && digits > zero_digits)
114: zero_digits = 0;
115: else
116: zero_digits -= digits;
117: // If there are no integer digits, add an initial '0', if there is room.
118: if (digits + zero_digits <= 0
119: && (width <= 0 || width > decimals + 1 + signLen))
120: zero_digits++;
121: int needed = signLen + length + zero_digits + 1; /* Add 1 for '.'. */
122: int padding = width - needed;
123: for (int i = zero_digits; --i >= 0;)
124: sbuf.insert(oldSize + signLen, '0');
125: if (padding >= 0) {
126: int i = oldSize;
127: if (internalPad && signLen > 0)
128: i++;
129: while (--padding >= 0)
130: sbuf.insert(i, padChar);
131: } else if (overflowChar != '\0') {
132: sbuf.setLength(oldSize);
133: for (i = width; --i >= 0;)
134: sbuf.append(overflowChar);
135: return;
136: }
137: int newSize = sbuf.length();
138: sbuf.insert(newSize - decimals, '.');
139: /* Requires JDK1.2 FieldPosition extensions:
140: if (fpos == null)
141: {
142: newSize++;
143: if (fpos.getField() == FRACTION_FIELD)
144: {
145: fpos.setBeginIndex(newSize-decimals);
146: fpos.setEndIndex(newSize);
147: }
148: else if (fpos.getField() == INTEGER_FIELD)
149: {
150: fpos.setBeginIndex(newSize-decimals);
151: fpos.setEndIndex(newSize-length-zero_digits-1);
152: }
153: }
154: */
155: }
156:
157: public void format(RatNum number, StringBuffer sbuf,
158: FieldPosition fpos) {
159: boolean negative = number.isNegative();
160: if (negative)
161: number = (RatNum) number.rneg();
162: format(number, negative, sbuf, fpos);
163: }
164:
165: public void format(RealNum number, StringBuffer sbuf,
166: FieldPosition fpos) {
167: if (number instanceof RatNum)
168: format((RatNum) number, sbuf, fpos);
169: else
170: format(number.doubleValue(), sbuf, fpos);
171: }
172:
173: public StringBuffer format(long num, StringBuffer sbuf,
174: FieldPosition fpos) {
175: format(IntNum.make(num), sbuf, fpos);
176: return sbuf;
177: }
178:
179: public StringBuffer format(double num, StringBuffer sbuf,
180: FieldPosition fpos) {
181: boolean negative;
182: if (num < 0) {
183: negative = true;
184: num = -num;
185: } else
186: negative = false;
187: format(DFloNum.toExact(num), negative, sbuf, fpos);
188: return sbuf;
189: }
190:
191: public StringBuffer format(Object num, StringBuffer sbuf,
192: FieldPosition fpos) {
193: // Common Lisp says if value is non-real, print as if with ~wD. FIXME.
194: return format(((RealNum) num).doubleValue(), sbuf, fpos);
195: }
196:
197: public java.lang.Number parse(String text,
198: java.text.ParsePosition status) {
199: throw new Error("RealFixedFormat.parse - not implemented");
200: }
201:
202: public Object parseObject(String text,
203: java.text.ParsePosition status) {
204: throw new Error("RealFixedFormat.parseObject - not implemented");
205: }
206:
207: }
|