1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.text.numbers;
18
19 import java.text.DecimalFormatSymbols;
20 import java.util.Objects;
21 import java.util.function.DoubleFunction;
22 import java.util.function.Function;
23 import java.util.function.Supplier;
24
25 /**
26 * Enum containing standard double format types with methods to produce
27 * configured formatter instances. This type is intended to provide a
28 * quick and convenient way to create lightweight, thread-safe double format functions
29 * for common format types using a builder pattern. Output can be localized by
30 * passing a {@link DecimalFormatSymbols} instance to the
31 * {@link Builder#formatSymbols(DecimalFormatSymbols) formatSymbols} method or by
32 * directly calling the various other builder configuration methods, such as
33 * {@link Builder#digits(String) digits}.
34 *
35 * <p><strong>Comparison with DecimalFormat</strong></p>
36 * <p>
37 * This type provides some of the same functionality as Java's own
38 * {@link java.text.DecimalFormat}. However, unlike {@code DecimalFormat}, the format
39 * functions produced by this type are lightweight and thread-safe, making them
40 * much easier to work with in multi-threaded environments. They also provide performance
41 * comparable to, and in many cases faster than, {@code DecimalFormat}.
42 * </p>
43 * <p>
44 * It should be noted that the output {@code String} is created by formatting the output of
45 * {@link Double#toString()}. This limits the output precision to the precision required
46 * to exactly represent the input {@code double} and is dependent on the JDK implementation
47 * of {@link Double#toString()}. A number formatted with the maximum
48 * precision should be parsed to the same input {@code double}. This implementation
49 * cannot extend the {@code String} to the required length to represent the exact decimal
50 * value of the {@code double} as per
51 * {@link java.math.BigDecimal#toString() BigDecimal#toString()}.
52 * </p>
53 * <p><strong>Examples</strong></p>
54 * <pre>
55 * // construct a formatter equivalent to Double.toString()
56 * DoubleFunction<String> fmt = DoubleFormat.MIXED.builder().build();
57 *
58 * // construct a formatter equivalent to Double.toString() but using
59 * // format symbols for a specific locale
60 * DoubleFunction<String> fmt = DoubleFormat.MIXED.builder()
61 * .formatSymbols(DecimalFormatSymbols.getInstance(locale))
62 * .build();
63 *
64 * // construct a formatter equivalent to the DecimalFormat pattern "0.0##"
65 * DoubleFunction<String> fmt = DoubleFormat.PLAIN.builder()
66 * .minDecimalExponent(-3)
67 * .build();
68 *
69 * // construct a formatter equivalent to the DecimalFormat pattern "#,##0.0##",
70 * // where whole number groups of thousands are separated
71 * DoubleFunction<String> fmt = DoubleFormat.PLAIN.builder()
72 * .minDecimalExponent(-3)
73 * .groupThousands(true)
74 * .build();
75 *
76 * // construct a formatter equivalent to the DecimalFormat pattern "0.0##E0"
77 * DoubleFunction<String> fmt = DoubleFormat.SCIENTIFIC.builder()
78 * .maxPrecision(4)
79 * .alwaysIncludeExponent(true)
80 * .build()
81 *
82 * // construct a formatter equivalent to the DecimalFormat pattern "##0.0##E0",
83 * // i.e. "engineering format"
84 * DoubleFunction<String> fmt = DoubleFormat.ENGINEERING.builder()
85 * .maxPrecision(6)
86 * .alwaysIncludeExponent(true)
87 * .build()
88 * </pre>
89 *
90 * <p><strong>Implementation Notes</strong></p>
91 * <p>
92 * {@link java.math.RoundingMode#HALF_EVEN Half-even} rounding is used in cases where the
93 * decimal value must be rounded in order to meet the configuration requirements of the formatter
94 * instance.
95 * </p>
96 *
97 * @since 1.10.0
98 */
99 public enum DoubleFormat {
100
101 /**
102 * Number format without exponents.
103 * <p>
104 * For example:
105 * </p>
106 *
107 * <pre>
108 * 0.0
109 * 12.401
110 * 100000.0
111 * 1450000000.0
112 * 0.0000000000123
113 * </pre>
114 */
115 PLAIN(PlainDoubleFormat::new),
116
117 /**
118 * Number format that uses exponents and contains a single digit to the left of the decimal point.
119 * <p>
120 * For example:
121 * </p>
122 *
123 * <pre>
124 * 0.0
125 * 1.2401E1
126 * 1.0E5
127 * 1.45E9
128 * 1.23E-11
129 * </pre>
130 */
131 SCIENTIFIC(ScientificDoubleFormat::new),
132
133 /**
134 * Number format similar to {@link #SCIENTIFIC scientific format} but adjusted so that the exponent value is always a multiple of 3, allowing easier
135 * alignment with SI prefixes.
136 * <p>
137 * For example:
138 * </p>
139 *
140 * <pre>
141 * 0.0
142 * 12.401
143 * 100.0E3
144 * 1.45E9
145 * 12.3E-12
146 * </pre>
147 */
148 ENGINEERING(EngineeringDoubleFormat::new),
149
150 /**
151 * Number format that uses {@link #PLAIN plain format} for small numbers and {@link #SCIENTIFIC scientific format} for large numbers. The number thresholds
152 * can be configured through the {@link Builder#plainFormatMinDecimalExponent(int) plainFormatMinDecimalExponent} and
153 * {@link Builder#plainFormatMaxDecimalExponent(int) plainFormatMaxDecimalExponent} properties.
154 * <p>
155 * For example:
156 * </p>
157 *
158 * <pre>
159 * 0.0
160 * 12.401
161 * 100000.0
162 * 1.45E9
163 * 1.23E-11
164 * </pre>
165 */
166 MIXED(MixedDoubleFormat::new);
167
168 /**
169 * Base class for standard double formatting classes.
170 */
171 private abstract static class AbstractDoubleFormat implements DoubleFunction<String>, ParsedDecimal.FormatOptions {
172
173 /** Maximum precision; 0 indicates no limit. */
174 private final int maxPrecision;
175
176 /** Minimum decimal exponent. */
177 private final int minDecimalExponent;
178
179 /** String representing positive infinity. */
180 private final String positiveInfinity;
181
182 /** String representing negative infinity. */
183 private final String negativeInfinity;
184
185 /** String representing NaN. */
186 private final String nan;
187
188 /** Flag determining if fraction placeholders should be used. */
189 private final boolean fractionPlaceholder;
190
191 /** Flag determining if signed zero strings are allowed. */
192 private final boolean signedZero;
193
194 /** String containing the digits 0-9. */
195 private final char[] digits;
196
197 /** Decimal separator character. */
198 private final char decimalSeparator;
199
200 /** Thousands grouping separator. */
201 private final char groupingSeparator;
202
203 /** Flag indicating if thousands should be grouped. */
204 private final boolean groupThousands;
205
206 /** Minus sign character. */
207 private final char minusSign;
208
209 /** Exponent separator character. */
210 private final char[] exponentSeparatorChars;
211
212 /** Flag indicating if exponent values should always be included, even if zero. */
213 private final boolean alwaysIncludeExponent;
214
215 /**
216 * Constructs a new instance.
217 *
218 * @param builder builder instance containing configuration values
219 */
220 AbstractDoubleFormat(final Builder builder) {
221 this.maxPrecision = builder.maxPrecision;
222 this.minDecimalExponent = builder.minDecimalExponent;
223
224 this.positiveInfinity = builder.infinity;
225 this.negativeInfinity = builder.minusSign + builder.infinity;
226 this.nan = builder.nan;
227
228 this.fractionPlaceholder = builder.fractionPlaceholder;
229 this.signedZero = builder.signedZero;
230 this.digits = builder.digits.toCharArray();
231 this.decimalSeparator = builder.decimalSeparator;
232 this.groupingSeparator = builder.groupingSeparator;
233 this.groupThousands = builder.groupThousands;
234 this.minusSign = builder.minusSign;
235 this.exponentSeparatorChars = builder.exponentSeparator.toCharArray();
236 this.alwaysIncludeExponent = builder.alwaysIncludeExponent;
237 }
238
239 /** {@inheritDoc} */
240 @Override
241 public String apply(final double d) {
242 if (Double.isFinite(d)) {
243 return applyFinite(d);
244 }
245 if (Double.isInfinite(d)) {
246 return d > 0.0 ? positiveInfinity : negativeInfinity;
247 }
248 return nan;
249 }
250
251 /**
252 * Returns a formatted string representation of the given finite value.
253 *
254 * @param d double value.
255 */
256 private String applyFinite(final double d) {
257 final ParsedDecimal n = ParsedDecimal.from(d);
258
259 int roundExponent = Math.max(n.getExponent(), minDecimalExponent);
260 if (maxPrecision > 0) {
261 roundExponent = Math.max(n.getScientificExponent() - maxPrecision + 1, roundExponent);
262 }
263 n.round(roundExponent);
264
265 return applyFiniteInternal(n);
266 }
267
268 /**
269 * Returns a formatted representation of the given rounded decimal value to {@code dst}.
270 *
271 * @param val value to format.
272 * @return a formatted representation of the given rounded decimal value to {@code dst}.
273 */
274 protected abstract String applyFiniteInternal(ParsedDecimal val);
275
276 /** {@inheritDoc} */
277 @Override
278 public char getDecimalSeparator() {
279 return decimalSeparator;
280 }
281
282 /** {@inheritDoc} */
283 @Override
284 public char[] getDigits() {
285 return digits;
286 }
287
288 /** {@inheritDoc} */
289 @Override
290 public char[] getExponentSeparatorChars() {
291 return exponentSeparatorChars;
292 }
293
294 /** {@inheritDoc} */
295 @Override
296 public char getGroupingSeparator() {
297 return groupingSeparator;
298 }
299
300 /** {@inheritDoc} */
301 @Override
302 public char getMinusSign() {
303 return minusSign;
304 }
305
306 /** {@inheritDoc} */
307 @Override
308 public boolean isAlwaysIncludeExponent() {
309 return alwaysIncludeExponent;
310 }
311
312 /** {@inheritDoc} */
313 @Override
314 public boolean isGroupThousands() {
315 return groupThousands;
316 }
317
318 /** {@inheritDoc} */
319 @Override
320 public boolean isIncludeFractionPlaceholder() {
321 return fractionPlaceholder;
322 }
323
324 /** {@inheritDoc} */
325 @Override
326 public boolean isSignedZero() {
327 return signedZero;
328 }
329 }
330
331 /**
332 * Builds configured format functions for standard double format types.
333 */
334 public static final class Builder implements Supplier<DoubleFunction<String>> {
335
336 /** Default value for the plain format max decimal exponent. */
337 private static final int DEFAULT_PLAIN_FORMAT_MAX_DECIMAL_EXPONENT = 6;
338
339 /** Default value for the plain format min decimal exponent. */
340 private static final int DEFAULT_PLAIN_FORMAT_MIN_DECIMAL_EXPONENT = -3;
341
342 /** Default decimal digit characters. */
343 private static final String DEFAULT_DECIMAL_DIGITS = "0123456789";
344
345 /**
346 * Gets a string containing the localized digits 0-9 for the given symbols object. The string is constructed by starting at the
347 * {@link DecimalFormatSymbols#getZeroDigit() zero digit} and adding the next 9 consecutive characters.
348 *
349 * @param symbols symbols object
350 * @return string containing the localized digits 0-9
351 */
352 private static String getDigitString(final DecimalFormatSymbols symbols) {
353 final int zeroDelta = symbols.getZeroDigit() - DEFAULT_DECIMAL_DIGITS.charAt(0);
354
355 final char[] digitChars = new char[DEFAULT_DECIMAL_DIGITS.length()];
356 for (int i = 0; i < DEFAULT_DECIMAL_DIGITS.length(); ++i) {
357 digitChars[i] = (char) (DEFAULT_DECIMAL_DIGITS.charAt(i) + zeroDelta);
358 }
359
360 return String.valueOf(digitChars);
361 }
362
363 /** Function used to construct format instances. */
364 private final Function<Builder, DoubleFunction<String>> factory;
365
366 /** Maximum number of significant decimal digits in formatted strings. */
367 private int maxPrecision;
368
369 /** Minimum decimal exponent. */
370 private int minDecimalExponent = Integer.MIN_VALUE;
371
372 /** Max decimal exponent to use with plain formatting with the mixed format type. */
373 private int plainFormatMaxDecimalExponent = DEFAULT_PLAIN_FORMAT_MAX_DECIMAL_EXPONENT;
374
375 /** Min decimal exponent to use with plain formatting with the mixed format type. */
376 private int plainFormatMinDecimalExponent = DEFAULT_PLAIN_FORMAT_MIN_DECIMAL_EXPONENT;
377
378 /** String representing infinity. */
379 private String infinity = "Infinity";
380
381 /** String representing NaN. */
382 private String nan = "NaN";
383
384 /** Flag determining if fraction placeholders should be used. */
385 private boolean fractionPlaceholder = true;
386
387 /** Flag determining if signed zero strings are allowed. */
388 private boolean signedZero = true;
389
390 /** String of digit characters 0-9. */
391 private String digits = DEFAULT_DECIMAL_DIGITS;
392
393 /** Decimal separator character. */
394 private char decimalSeparator = '.';
395
396 /** Character used to separate groups of thousands. */
397 private char groupingSeparator = ',';
398
399 /** If {@code true}, thousands groups will be separated by the grouping separator. */
400 private boolean groupThousands;
401
402 /** Minus sign character. */
403 private char minusSign = '-';
404
405 /** Exponent separator character. */
406 private String exponentSeparator = "E";
407
408 /** Flag indicating if the exponent value should always be included, even if zero. */
409 private boolean alwaysIncludeExponent;
410
411 /**
412 * Builds a new instance that delegates double function construction to the given factory object.
413 *
414 * @param factory factory function.
415 */
416 private Builder(final Function<Builder, DoubleFunction<String>> factory) {
417 this.factory = factory;
418 }
419
420 /**
421 * Sets the flag determining whether or not the zero string may be returned with the minus sign or if it will always be returned in the positive form.
422 * For example, if set to {@code true}, the string {@code "-0.0"} may be returned for some input numbers. If {@code false}, only {@code "0.0"} will be
423 * returned, regardless of the sign of the input number. The default value is {@code true}.
424 *
425 * @param signedZero if {@code true}, the zero string may be returned with a preceding minus sign; if {@code false}, the zero string will only be
426 * returned in its positive form.
427 * @return {@code this} instance.
428 */
429 public Builder allowSignedZero(final boolean signedZero) {
430 this.signedZero = signedZero;
431 return this;
432 }
433
434 /**
435 * Sets the flag indicating if an exponent value should always be included in the formatted value, even if the exponent value is zero. This property
436 * only applies to formats that use scientific notation, namely {@link DoubleFormat#SCIENTIFIC SCIENTIFIC}, {@link DoubleFormat#ENGINEERING
437 * ENGINEERING}, and {@link DoubleFormat#MIXED MIXED}. The default value is {@code false}.
438 *
439 * @param alwaysIncludeExponent if {@code true}, exponents will always be included in formatted output even if the exponent value is zero.
440 * @return {@code this} instance.
441 */
442 public Builder alwaysIncludeExponent(final boolean alwaysIncludeExponent) {
443 this.alwaysIncludeExponent = alwaysIncludeExponent;
444 return this;
445 }
446
447 /**
448 * Builds a new double format function.
449 *
450 * @return format function
451 * @deprecated Use {@link #get()}.
452 */
453 @Deprecated
454 public DoubleFunction<String> build() {
455 return get();
456 }
457
458 /**
459 * Sets the decimal separator character, i.e., the character placed between the whole number and fractional portions of the formatted strings. The
460 * default value is {@code '.'}.
461 *
462 * @param decimalSeparator decimal separator character.
463 * @return {@code this} instance.
464 */
465 public Builder decimalSeparator(final char decimalSeparator) {
466 this.decimalSeparator = decimalSeparator;
467 return this;
468 }
469
470 /**
471 * Sets the string containing the digit characters 0-9, in that order. The default value is the string {@code "0123456789"}.
472 *
473 * @param digits string containing the digit characters 0-9.
474 * @return {@code this} instance.
475 * @throws NullPointerException if the argument is {@code null}.
476 * @throws IllegalArgumentException if the argument does not have a length of exactly 10.
477 */
478 public Builder digits(final String digits) {
479 Objects.requireNonNull(digits, "digits");
480 if (digits.length() != DEFAULT_DECIMAL_DIGITS.length()) {
481 throw new IllegalArgumentException("Digits string must contain exactly " + DEFAULT_DECIMAL_DIGITS.length() + " characters.");
482 }
483
484 this.digits = digits;
485 return this;
486 }
487
488 /**
489 * Sets the exponent separator character, i.e., the string placed between the mantissa and the exponent. The default value is {@code "E"}, as in
490 * {@code "1.2E6"}.
491 *
492 * @param exponentSeparator exponent separator string.
493 * @return {@code this} instance.
494 * @throws NullPointerException if the argument is {@code null}.
495 */
496 public Builder exponentSeparator(final String exponentSeparator) {
497 this.exponentSeparator = Objects.requireNonNull(exponentSeparator, "exponentSeparator");
498 return this;
499 }
500
501 /**
502 * Configures this instance with the given format symbols. The following values are set:
503 * <ul>
504 * <li>{@link #digits(String) digit characters}</li>
505 * <li>{@link #decimalSeparator(char) decimal separator}</li>
506 * <li>{@link #groupingSeparator(char) thousands grouping separator}</li>
507 * <li>{@link #minusSign(char) minus sign}</li>
508 * <li>{@link #exponentSeparator(String) exponent separator}</li>
509 * <li>{@link #infinity(String) infinity}</li>
510 * <li>{@link #nan(String) NaN}</li>
511 * </ul>
512 * The digit character string is constructed by starting at the configured {@link DecimalFormatSymbols#getZeroDigit() zero digit} and adding the next 9
513 * consecutive characters.
514 *
515 * @param symbols format symbols.
516 * @return {@code this} instance.
517 * @throws NullPointerException if the argument is {@code null}.
518 */
519 public Builder formatSymbols(final DecimalFormatSymbols symbols) {
520 Objects.requireNonNull(symbols, "symbols");
521
522 return digits(getDigitString(symbols)).decimalSeparator(symbols.getDecimalSeparator()).groupingSeparator(symbols.getGroupingSeparator())
523 .minusSign(symbols.getMinusSign()).exponentSeparator(symbols.getExponentSeparator()).infinity(symbols.getInfinity()).nan(symbols.getNaN());
524 }
525
526 /**
527 * Builds a new double format function.
528 *
529 * @return a new double format function.
530 */
531 @Override
532 public DoubleFunction<String> get() {
533 return factory.apply(this);
534 }
535
536 /**
537 * Sets the character used to separate groups of thousands. Default value is {@code ','}.
538 *
539 * @param groupingSeparator character used to separate groups of thousands.
540 * @return {@code this} instance.
541 * @see #groupThousands(boolean)
542 */
543 public Builder groupingSeparator(final char groupingSeparator) {
544 this.groupingSeparator = groupingSeparator;
545 return this;
546 }
547
548 /**
549 * If set to {@code true}, thousands will be grouped with the {@link #groupingSeparator(char) grouping separator}. For example, if set to {@code true},
550 * the number {@code 1000} could be formatted as {@code "1,000"}. This property only applies to the {@link DoubleFormat#PLAIN PLAIN} format. Default
551 * value is {@code false}.
552 *
553 * @param groupThousands if {@code true}, thousands will be grouped.
554 * @return {@code this} instance.
555 * @see #groupingSeparator(char)
556 */
557 public Builder groupThousands(final boolean groupThousands) {
558 this.groupThousands = groupThousands;
559 return this;
560 }
561
562 /**
563 * Sets the flag determining whether or not a zero character is added in the fraction position when no fractional value is present. For example, if set
564 * to {@code true}, the number {@code 1} would be formatted as {@code "1.0"}. If {@code false}, it would be formatted as {@code "1"}. The default value
565 * is {@code true}.
566 *
567 * @param fractionPlaceholder if {@code true}, a zero character is placed in the fraction position when no fractional value is present; if
568 * {@code false}, fractional digits are only included when needed.
569 * @return {@code this} instance.
570 */
571 public Builder includeFractionPlaceholder(final boolean fractionPlaceholder) {
572 this.fractionPlaceholder = fractionPlaceholder;
573 return this;
574 }
575
576 /**
577 * Sets the string used to represent infinity. For negative infinity, this string is prefixed with the {@link #minusSign(char) minus sign}.
578 *
579 * @param infinity string used to represent infinity.
580 * @return {@code this} instance.
581 * @throws NullPointerException if the argument is {@code null}.
582 */
583 public Builder infinity(final String infinity) {
584 this.infinity = Objects.requireNonNull(infinity, "infinity");
585 return this;
586 }
587
588 /**
589 * Sets the maximum number of significant decimal digits used in format results. A value of {@code 0} indicates no limit. The default value is
590 * {@code 0}.
591 *
592 * @param maxPrecision maximum precision.
593 * @return {@code this} instance.
594 */
595 public Builder maxPrecision(final int maxPrecision) {
596 this.maxPrecision = maxPrecision;
597 return this;
598 }
599
600 /**
601 * Sets the minimum decimal exponent for formatted strings. No digits with an absolute value of less than <code>10<sup>minDecimalExponent</sup></code>
602 * will be included in format results. If the number being formatted does not contain any such digits, then zero is returned. For example, if
603 * {@code minDecimalExponent} is set to {@code -2} and the number {@code 3.14159} is formatted, the plain format result will be {@code "3.14"}. If
604 * {@code 0.001} is formatted, then the result is the zero string.
605 *
606 * @param minDecimalExponent minimum decimal exponent.
607 * @return {@code this} instance.
608 */
609 public Builder minDecimalExponent(final int minDecimalExponent) {
610 this.minDecimalExponent = minDecimalExponent;
611 return this;
612 }
613
614 /**
615 * Sets the character used as the minus sign.
616 *
617 * @param minusSign character to use as the minus sign.
618 * @return {@code this} instance.
619 */
620 public Builder minusSign(final char minusSign) {
621 this.minusSign = minusSign;
622 return this;
623 }
624
625 /**
626 * Sets the string used to represent {@link Double#NaN}.
627 *
628 * @param nan string used to represent {@link Double#NaN}.
629 * @return {@code this} instance.
630 * @throws NullPointerException if the argument is {@code null}.
631 */
632 public Builder nan(final String nan) {
633 this.nan = Objects.requireNonNull(nan, "nan");
634 return this;
635 }
636
637 /**
638 * Sets the maximum decimal exponent for numbers formatted as plain decimal strings when using the {@link DoubleFormat#MIXED MIXED} format type.
639 * <p>
640 * If the number being formatted has an absolute value less than <code>10<sup>plainFormatMaxDecimalExponent + 1</sup></code> and greater than or equal
641 * to <code>10<sup>plainFormatMinDecimalExponent</sup></code> after any necessary rounding, then the formatted result will use the
642 * {@link DoubleFormat#PLAIN PLAIN} format type. Otherwise, {@link DoubleFormat#SCIENTIFIC SCIENTIFIC} format will be used. For example, if this value
643 * is set to {@code 2}, the number {@code 999} will be formatted as {@code "999.0"} while {@code 1000} will be formatted as {@code "1.0E3"}.
644 * </p>
645 * <p>
646 * The default value is {@code 6}.
647 * </p>
648 * <p>
649 * This value is ignored for formats other than {@link DoubleFormat#MIXED}.
650 * </p>
651 *
652 * @param plainFormatMaxDecimalExponent maximum decimal exponent for values formatted as plain strings when using the {@link DoubleFormat#MIXED MIXED}
653 * format type.
654 * @return {@code this} instance
655 * @see #plainFormatMinDecimalExponent(int)
656 */
657 public Builder plainFormatMaxDecimalExponent(final int plainFormatMaxDecimalExponent) {
658 this.plainFormatMaxDecimalExponent = plainFormatMaxDecimalExponent;
659 return this;
660 }
661
662 /**
663 * Sets the minimum decimal exponent for numbers formatted as plain decimal strings when using the {@link DoubleFormat#MIXED MIXED} format type.
664 * <p>
665 * If the number being formatted has an absolute value less than <code>10<sup>plainFormatMaxDecimalExponent + 1</sup></code> and greater than or equal
666 * to <code>10<sup>plainFormatMinDecimalExponent</sup></code> after any necessary rounding, then the formatted result will use the
667 * {@link DoubleFormat#PLAIN PLAIN} format type. Otherwise, {@link DoubleFormat#SCIENTIFIC SCIENTIFIC} format will be used. For example, if this value
668 * is set to {@code -2}, the number {@code 0.01} will be formatted as {@code "0.01"} while {@code 0.0099} will be formatted as {@code "9.9E-3"}.
669 * </p>
670 * <p>
671 * The default value is {@code -3}.
672 * </p>
673 * <p>
674 * This value is ignored for formats other than {@link DoubleFormat#MIXED}.
675 * </p>
676 *
677 * @param plainFormatMinDecimalExponent maximum decimal exponent for values formatted as plain strings when using the {@link DoubleFormat#MIXED MIXED}
678 * format type.
679 * @return {@code this} instance
680 * @see #plainFormatMinDecimalExponent(int)
681 */
682 public Builder plainFormatMinDecimalExponent(final int plainFormatMinDecimalExponent) {
683 this.plainFormatMinDecimalExponent = plainFormatMinDecimalExponent;
684 return this;
685 }
686 }
687
688 /**
689 * Format class that uses engineering notation for all values.
690 */
691 private static final class EngineeringDoubleFormat extends AbstractDoubleFormat {
692
693 /**
694 * Constructs a new instance.
695 *
696 * @param builder builder instance containing configuration values
697 */
698 EngineeringDoubleFormat(final Builder builder) {
699 super(builder);
700 }
701
702 /** {@inheritDoc} */
703 @Override
704 public String applyFiniteInternal(final ParsedDecimal val) {
705 return val.toEngineeringString(this);
706 }
707 }
708
709 /**
710 * Format class producing results similar to {@link Double#toString()}, with plain decimal notation for small numbers relatively close to zero and
711 * scientific notation otherwise.
712 */
713 private static final class MixedDoubleFormat extends AbstractDoubleFormat {
714
715 /** Max decimal exponent for plain format. */
716 private final int plainMaxExponent;
717
718 /** Min decimal exponent for plain format. */
719 private final int plainMinExponent;
720
721 /**
722 * Constructs a new instance.
723 *
724 * @param builder builder instance containing configuration values
725 */
726 MixedDoubleFormat(final Builder builder) {
727 super(builder);
728
729 this.plainMaxExponent = builder.plainFormatMaxDecimalExponent;
730 this.plainMinExponent = builder.plainFormatMinDecimalExponent;
731 }
732
733 /** {@inheritDoc} */
734 @Override
735 protected String applyFiniteInternal(final ParsedDecimal val) {
736 final int sciExp = val.getScientificExponent();
737 if (sciExp <= plainMaxExponent && sciExp >= plainMinExponent) {
738 return val.toPlainString(this);
739 }
740 return val.toScientificString(this);
741 }
742 }
743
744 /**
745 * Format class that produces plain decimal strings that do not use scientific notation.
746 */
747 private static final class PlainDoubleFormat extends AbstractDoubleFormat {
748
749 /**
750 * Constructs a new instance.
751 *
752 * @param builder builder instance containing configuration values
753 */
754 PlainDoubleFormat(final Builder builder) {
755 super(builder);
756 }
757
758 /**
759 * {@inheritDoc}
760 */
761 @Override
762 protected String applyFiniteInternal(final ParsedDecimal val) {
763 return val.toPlainString(this);
764 }
765 }
766
767 /**
768 * Format class that uses scientific notation for all values.
769 */
770 private static final class ScientificDoubleFormat extends AbstractDoubleFormat {
771
772 /**
773 * Constructs a new instance.
774 *
775 * @param builder builder instance containing configuration values
776 */
777 ScientificDoubleFormat(final Builder builder) {
778 super(builder);
779 }
780
781 /** {@inheritDoc} */
782 @Override
783 public String applyFiniteInternal(final ParsedDecimal val) {
784 return val.toScientificString(this);
785 }
786 }
787
788 /** Function used to construct instances for this format type. */
789 private final Function<Builder, DoubleFunction<String>> factory;
790
791 /**
792 * Constructs a new instance.
793 *
794 * @param factory function used to construct format instances
795 */
796 DoubleFormat(final Function<Builder, DoubleFunction<String>> factory) {
797 this.factory = factory;
798 }
799
800 /**
801 * Creates a {@link Builder} for building formatter functions for this format type.
802 *
803 * @return builder instance
804 */
805 public Builder builder() {
806 return new Builder(factory);
807 }
808 }