FixedRealFormat.java 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. // Copyright (c) 1999, 2009 Per M.A. Bothner.
  2. // This is free software; for terms and warranty disclaimer see ./COPYING.
  3. package gnu.math;
  4. import java.text.FieldPosition;
  5. // Can't use NumberFormat, because its format(Object, StringBuffer,
  6. // FieldPosition) method is final - and does the wrong thing.
  7. // (It ends up converting gnu.math number types to long!)
  8. /** Format a real number using a fixed-point format.
  9. * Used for Common Lisp specs ~F and ~$; also C-style %f.
  10. */
  11. public class FixedRealFormat extends java.text.Format
  12. {
  13. private int i, d;
  14. public int getMaximumFractionDigits() { return d; }
  15. public int getMinimumIntegerDigits() { return i; }
  16. public void setMaximumFractionDigits(int d) { this.d = d; }
  17. public void setMinimumIntegerDigits(int i) { this.i = i; }
  18. // These should not be public. FIXME.
  19. public int width;
  20. public int scale;
  21. public char padChar;
  22. public boolean showPlus;
  23. public boolean internalPad;
  24. public char overflowChar;
  25. public void format(RealNum number, StringBuffer sbuf, FieldPosition fpos)
  26. {
  27. int decimals;
  28. if (number instanceof RatNum
  29. && (decimals = getMaximumFractionDigits()) >= 0)
  30. {
  31. RatNum ratnum = (RatNum) number;
  32. boolean negative = ratnum.isNegative();
  33. if (negative)
  34. ratnum = ratnum.rneg();
  35. int oldSize = sbuf.length();
  36. int signLen = 1;
  37. if (negative)
  38. sbuf.append('-');
  39. else if (showPlus)
  40. sbuf.append('+');
  41. else
  42. signLen = 0;
  43. String string = RealNum.toScaledInt(ratnum, decimals+scale)
  44. .toString();
  45. sbuf.append(string);
  46. int length = string.length();
  47. int digits = length - decimals;
  48. format(sbuf, fpos, length, digits, decimals, signLen, oldSize);
  49. }
  50. else
  51. format(number.doubleValue(), sbuf, fpos);
  52. }
  53. public StringBuffer format(long num, StringBuffer sbuf, FieldPosition fpos)
  54. {
  55. format(IntNum.make(num), sbuf, fpos);
  56. return sbuf;
  57. }
  58. public StringBuffer format(double num, StringBuffer sbuf, FieldPosition fpos)
  59. {
  60. if (Double.isNaN(num) || Double.isInfinite(num))
  61. return sbuf.append(num);
  62. if (getMaximumFractionDigits() >= 0)
  63. format(DFloNum.toExact(num), sbuf, fpos);
  64. else
  65. {
  66. boolean negative;
  67. if (num < 0)
  68. {
  69. negative = true;
  70. num = -num;
  71. }
  72. else
  73. negative = false;
  74. int oldSize = sbuf.length();
  75. int signLen = 1;
  76. if (negative)
  77. sbuf.append('-');
  78. else if (showPlus)
  79. sbuf.append('+');
  80. else
  81. signLen = 0;
  82. String string = Double.toString(num);
  83. int cur_scale = scale;
  84. int seenE = string.indexOf('E');
  85. if (seenE >= 0)
  86. {
  87. int expStart = seenE+1;
  88. if (string.charAt(expStart) == '+')
  89. expStart++;
  90. cur_scale += Integer.parseInt(string.substring(expStart));
  91. string = string.substring(0, seenE);
  92. }
  93. int seenDot = string.indexOf('.');
  94. int length = string.length();
  95. if (seenDot >= 0) // Should always be true.
  96. {
  97. cur_scale -= length - seenDot - 1;
  98. length--;
  99. string = string.substring(0, seenDot) + string.substring(seenDot+1);
  100. }
  101. int i = string.length();
  102. // Skip leading zeros. May happen if scale != 0.
  103. int initial_zeros = 0;
  104. while (initial_zeros < i - 1 && string.charAt(initial_zeros) == '0')
  105. initial_zeros++;
  106. if (initial_zeros > 0)
  107. {
  108. string = string.substring(initial_zeros);
  109. i -= initial_zeros;
  110. }
  111. int decimals;
  112. int digits = i + cur_scale;
  113. if (width > 0)
  114. {
  115. // Add zeros after the decimal point. This could be made implicit,
  116. // but that would complicate rounding to fit in the field width.
  117. while (digits < 0)
  118. {
  119. sbuf.append('0');
  120. digits++;
  121. i++;
  122. }
  123. decimals = width - signLen - 1 - digits;
  124. }
  125. else
  126. decimals = (i > 16 ? 16 : i) - digits;
  127. if (decimals < 0)
  128. decimals = 0;
  129. sbuf.append(string);
  130. while (cur_scale > 0)
  131. {
  132. sbuf.append('0');
  133. cur_scale--;
  134. i++;
  135. }
  136. int digStart = oldSize + signLen;
  137. int digEnd = digStart + digits + decimals;
  138. i = sbuf.length();
  139. char nextDigit;
  140. if (digEnd >= i)
  141. {
  142. digEnd = i;
  143. nextDigit = '0';
  144. }
  145. else
  146. nextDigit = sbuf.charAt(digEnd);
  147. boolean addOne = nextDigit >= '5';
  148. char skip = addOne ? '9' : '0';
  149. while (digEnd > digStart + digits && sbuf.charAt(digEnd - 1) == skip)
  150. digEnd--;
  151. length = digEnd - digStart;
  152. decimals = length - digits;
  153. if (addOne)
  154. {
  155. if (ExponentialFormat.addOne(sbuf, digStart, digEnd))
  156. {
  157. digits++;
  158. decimals = 0;
  159. length = digits;
  160. }
  161. }
  162. if (decimals == 0 && (width <= 0
  163. || signLen + digits + 1 < width))
  164. {
  165. decimals = 1;
  166. length++;
  167. // This is only needed if number==0.0:
  168. sbuf.insert(digStart+digits, '0');
  169. }
  170. sbuf.setLength(digStart + length);
  171. format(sbuf, fpos, length, digits, decimals,
  172. negative ? 1 : 0,
  173. oldSize);
  174. }
  175. return sbuf;
  176. }
  177. public StringBuffer format(Object num, StringBuffer sbuf, FieldPosition fpos)
  178. {
  179. RealNum rnum = RealNum.asRealNumOrNull(num);
  180. if (rnum == null)
  181. {
  182. if (num instanceof Complex)
  183. {
  184. // Common Lisp says if value is non-real, print as if with ~wD.
  185. String str = num.toString();
  186. int padding = width - str.length();
  187. while (--padding >= 0)
  188. sbuf.append(' ');
  189. sbuf.append(str);
  190. return sbuf;
  191. }
  192. rnum = (RealNum) num;
  193. }
  194. return format(rnum.doubleValue(), sbuf, fpos);
  195. }
  196. /** Do padding and similar adjustments on the converted number. */
  197. private void format (StringBuffer sbuf, FieldPosition fpos, int length, int digits, int decimals, int signLen, int oldSize)
  198. {
  199. int total_digits = digits + decimals;
  200. // Number of initial zeros to add.
  201. int zero_digits = getMinimumIntegerDigits();
  202. if (digits >= 0 && digits > zero_digits)
  203. zero_digits = 0;
  204. else
  205. zero_digits -= digits;
  206. // If there are no integer digits, add an initial '0', if there is room.
  207. if (digits + zero_digits <= 0
  208. && (width <= 0 || width > decimals + 1 + signLen))
  209. zero_digits++;
  210. int needed = signLen + length + zero_digits + 1; /* Add 1 for '.'. */
  211. int padding = width - needed;
  212. for (int i = zero_digits; --i >= 0; )
  213. sbuf.insert(oldSize + signLen, '0');
  214. if (padding >= 0)
  215. {
  216. int i = oldSize;
  217. if (internalPad && signLen > 0)
  218. i++;
  219. while (--padding >= 0)
  220. sbuf.insert(i, padChar);
  221. }
  222. else if (overflowChar != '\0')
  223. {
  224. sbuf.setLength(oldSize);
  225. for (i = width; --i >= 0; )
  226. sbuf.append(overflowChar);
  227. return;
  228. }
  229. int newSize = sbuf.length();
  230. sbuf.insert(newSize - decimals, '.');
  231. /* Requires JDK1.2 FieldPosition extensions:
  232. if (fpos == null)
  233. {
  234. newSize++;
  235. if (fpos.getField() == FRACTION_FIELD)
  236. {
  237. fpos.setBeginIndex(newSize-decimals);
  238. fpos.setEndIndex(newSize);
  239. }
  240. else if (fpos.getField() == INTEGER_FIELD)
  241. {
  242. fpos.setBeginIndex(newSize-decimals);
  243. fpos.setEndIndex(newSize-length-zero_digits-1);
  244. }
  245. }
  246. */
  247. }
  248. public java.lang.Number parse(String text, java.text.ParsePosition status) {
  249. throw new UnsupportedOperationException
  250. ("RealFixedFormat.parse - not implemented");
  251. }
  252. public Object parseObject(String text, java.text.ParsePosition status) {
  253. throw new UnsupportedOperationException
  254. ("RealFixedFormat.parseObject - not implemented");
  255. }
  256. }