You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NumberFormatter.java 6.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. Copyright (c) 2018 James Ahlborn
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package com.healthmarketscience.jackcess.impl.expr;
  14. import java.math.BigDecimal;
  15. import java.math.MathContext;
  16. import java.math.RoundingMode;
  17. import java.text.DecimalFormat;
  18. import java.text.FieldPosition;
  19. import java.text.NumberFormat;
  20. import java.text.ParsePosition;
  21. /**
  22. *
  23. * @author James Ahlborn
  24. */
  25. public class NumberFormatter
  26. {
  27. public static final RoundingMode ROUND_MODE = RoundingMode.HALF_EVEN;
  28. /** designates the format of exponent notation used by ScientificFormat */
  29. public enum NotationType {
  30. /** Scientific notation "E", "E-" (default java behavior) */
  31. EXP_E_MINUS {
  32. @Override
  33. protected void format(StringBuffer sb, int eIdx) {
  34. // nothing to do
  35. }
  36. },
  37. /** Scientific notation "E+", "E-" */
  38. EXP_E_PLUS {
  39. @Override
  40. protected void format(StringBuffer sb, int eIdx) {
  41. maybeInsertExpPlus(sb, eIdx);
  42. }
  43. },
  44. /** Scientific notation "e", "e-" */
  45. EXP_e_MINUS {
  46. @Override
  47. protected void format(StringBuffer sb, int eIdx) {
  48. sb.setCharAt(eIdx, 'e');
  49. }
  50. },
  51. /** Scientific notation "e+", "e-" */
  52. EXP_e_PLUS {
  53. @Override
  54. protected void format(StringBuffer sb, int eIdx) {
  55. sb.setCharAt(eIdx, 'e');
  56. maybeInsertExpPlus(sb, eIdx);
  57. }
  58. };
  59. protected abstract void format(StringBuffer sb, int idx);
  60. }
  61. private static final int FLT_SIG_DIGITS = 7;
  62. private static final int DBL_SIG_DIGITS = 15;
  63. private static final int DEC_SIG_DIGITS = 28;
  64. public static final MathContext FLT_MATH_CONTEXT =
  65. new MathContext(FLT_SIG_DIGITS, ROUND_MODE);
  66. public static final MathContext DBL_MATH_CONTEXT =
  67. new MathContext(DBL_SIG_DIGITS, ROUND_MODE);
  68. public static final MathContext DEC_MATH_CONTEXT =
  69. new MathContext(DEC_SIG_DIGITS, ROUND_MODE);
  70. // note, java doesn't distinguish between pos/neg NaN
  71. private static final String NAN_STR = "1.#QNAN";
  72. private static final String POS_INF_STR = "1.#INF";
  73. private static final String NEG_INf_STR = "-1.#INF";
  74. private static final ThreadLocal<NumberFormatter> INSTANCE =
  75. new ThreadLocal<NumberFormatter>() {
  76. @Override
  77. protected NumberFormatter initialValue() {
  78. return new NumberFormatter();
  79. }
  80. };
  81. private final TypeFormatter _fltFmt = new TypeFormatter(FLT_SIG_DIGITS);
  82. private final TypeFormatter _dblFmt = new TypeFormatter(DBL_SIG_DIGITS);
  83. private final TypeFormatter _decFmt = new TypeFormatter(DEC_SIG_DIGITS);
  84. private NumberFormatter() {}
  85. public static String format(float f) {
  86. return INSTANCE.get().formatImpl(f);
  87. }
  88. public static String format(double d) {
  89. return INSTANCE.get().formatImpl(d);
  90. }
  91. public static String format(BigDecimal bd) {
  92. return INSTANCE.get().formatImpl(bd);
  93. }
  94. private String formatImpl(float f) {
  95. if(Float.isNaN(f)) {
  96. return NAN_STR;
  97. }
  98. if(Float.isInfinite(f)) {
  99. return ((f < 0f) ? NEG_INf_STR : POS_INF_STR);
  100. }
  101. return _fltFmt.format(new BigDecimal(f, FLT_MATH_CONTEXT));
  102. }
  103. private String formatImpl(double d) {
  104. if(Double.isNaN(d)) {
  105. return NAN_STR;
  106. }
  107. if(Double.isInfinite(d)) {
  108. return ((d < 0d) ? NEG_INf_STR : POS_INF_STR);
  109. }
  110. return _dblFmt.format(new BigDecimal(d, DBL_MATH_CONTEXT));
  111. }
  112. private String formatImpl(BigDecimal bd) {
  113. return _decFmt.format(bd.round(DEC_MATH_CONTEXT));
  114. }
  115. private static ScientificFormat createScientificFormat(int prec) {
  116. DecimalFormat df = new DecimalFormat("0.#E00");
  117. df.setMaximumIntegerDigits(1);
  118. df.setMaximumFractionDigits(prec);
  119. df.setRoundingMode(ROUND_MODE);
  120. return new ScientificFormat(df);
  121. }
  122. private static final class TypeFormatter
  123. {
  124. private final DecimalFormat _df = new DecimalFormat("0.#");
  125. private final ScientificFormat _dfS;
  126. private final int _prec;
  127. private TypeFormatter(int prec) {
  128. _prec = prec;
  129. _df.setMaximumIntegerDigits(prec);
  130. _df.setMaximumFractionDigits(prec);
  131. _df.setRoundingMode(ROUND_MODE);
  132. _dfS = createScientificFormat(prec);
  133. }
  134. public String format(BigDecimal bd) {
  135. bd = bd.stripTrailingZeros();
  136. int prec = bd.precision();
  137. int scale = bd.scale();
  138. int sigDigits = prec;
  139. if(scale < 0) {
  140. sigDigits -= scale;
  141. } else if(scale > prec) {
  142. sigDigits += (scale - prec);
  143. }
  144. return ((sigDigits > _prec) ? _dfS.format(bd) : _df.format(bd));
  145. }
  146. }
  147. private static void maybeInsertExpPlus(StringBuffer sb, int eIdx) {
  148. if(sb.charAt(eIdx + 1) != '-') {
  149. sb.insert(eIdx + 1, '+');
  150. }
  151. }
  152. public static class ScientificFormat extends NumberFormat
  153. {
  154. private static final long serialVersionUID = 0L;
  155. private final NumberFormat _df;
  156. private final NotationType _type;
  157. public ScientificFormat(NumberFormat df) {
  158. this(df, NotationType.EXP_E_PLUS);
  159. }
  160. public ScientificFormat(NumberFormat df, NotationType type) {
  161. _df = df;
  162. _type = type;
  163. }
  164. @Override
  165. public StringBuffer format(Object number, StringBuffer toAppendTo,
  166. FieldPosition pos)
  167. {
  168. StringBuffer sb = _df.format(number, toAppendTo, pos);
  169. _type.format(sb, sb.lastIndexOf("E"));
  170. return sb;
  171. }
  172. @Override
  173. public StringBuffer format(double number, StringBuffer toAppendTo,
  174. FieldPosition pos) {
  175. throw new UnsupportedOperationException();
  176. }
  177. @Override
  178. public Number parse(String source, ParsePosition parsePosition) {
  179. throw new UnsupportedOperationException();
  180. }
  181. @Override
  182. public StringBuffer format(long number, StringBuffer toAppendTo,
  183. FieldPosition pos) {
  184. throw new UnsupportedOperationException();
  185. }
  186. @Override
  187. public int getMaximumFractionDigits() {
  188. return _df.getMaximumFractionDigits();
  189. }
  190. @Override
  191. public int getMinimumFractionDigits() {
  192. return _df.getMinimumFractionDigits();
  193. }
  194. }
  195. }