001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.text.numbers; 018 019import java.text.DecimalFormatSymbols; 020import java.util.Objects; 021import java.util.function.DoubleFunction; 022import java.util.function.Function; 023import java.util.function.Supplier; 024 025/** 026 * Enum containing standard double format types with methods to produce 027 * configured formatter instances. This type is intended to provide a 028 * quick and convenient way to create lightweight, thread-safe double format functions 029 * for common format types using a builder pattern. Output can be localized by 030 * passing a {@link DecimalFormatSymbols} instance to the 031 * {@link Builder#formatSymbols(DecimalFormatSymbols) formatSymbols} method or by 032 * directly calling the various other builder configuration methods, such as 033 * {@link Builder#digits(String) digits}. 034 * 035 * <p><strong>Comparison with DecimalFormat</strong></p> 036 * <p> 037 * This type provides some of the same functionality as Java's own 038 * {@link java.text.DecimalFormat}. However, unlike {@code DecimalFormat}, the format 039 * functions produced by this type are lightweight and thread-safe, making them 040 * much easier to work with in multi-threaded environments. They also provide performance 041 * comparable to, and in many cases faster than, {@code DecimalFormat}. 042 * </p> 043 * <p> 044 * It should be noted that the output {@code String} is created by formatting the output of 045 * {@link Double#toString()}. This limits the output precision to the precision required 046 * to exactly represent the input {@code double} and is dependent on the JDK implementation 047 * of {@link Double#toString()}. A number formatted with the maximum 048 * precision should be parsed to the same input {@code double}. This implementation 049 * cannot extend the {@code String} to the required length to represent the exact decimal 050 * value of the {@code double} as per 051 * {@link java.math.BigDecimal#toString() BigDecimal#toString()}. 052 * </p> 053 * <p><strong>Examples</strong></p> 054 * <pre> 055 * // construct a formatter equivalent to Double.toString() 056 * DoubleFunction<String> fmt = DoubleFormat.MIXED.builder().build(); 057 * 058 * // construct a formatter equivalent to Double.toString() but using 059 * // format symbols for a specific locale 060 * DoubleFunction<String> fmt = DoubleFormat.MIXED.builder() 061 * .formatSymbols(DecimalFormatSymbols.getInstance(locale)) 062 * .build(); 063 * 064 * // construct a formatter equivalent to the DecimalFormat pattern "0.0##" 065 * DoubleFunction<String> fmt = DoubleFormat.PLAIN.builder() 066 * .minDecimalExponent(-3) 067 * .build(); 068 * 069 * // construct a formatter equivalent to the DecimalFormat pattern "#,##0.0##", 070 * // where whole number groups of thousands are separated 071 * DoubleFunction<String> fmt = DoubleFormat.PLAIN.builder() 072 * .minDecimalExponent(-3) 073 * .groupThousands(true) 074 * .build(); 075 * 076 * // construct a formatter equivalent to the DecimalFormat pattern "0.0##E0" 077 * DoubleFunction<String> fmt = DoubleFormat.SCIENTIFIC.builder() 078 * .maxPrecision(4) 079 * .alwaysIncludeExponent(true) 080 * .build() 081 * 082 * // construct a formatter equivalent to the DecimalFormat pattern "##0.0##E0", 083 * // i.e. "engineering format" 084 * DoubleFunction<String> fmt = DoubleFormat.ENGINEERING.builder() 085 * .maxPrecision(6) 086 * .alwaysIncludeExponent(true) 087 * .build() 088 * </pre> 089 * 090 * <p><strong>Implementation Notes</strong></p> 091 * <p> 092 * {@link java.math.RoundingMode#HALF_EVEN Half-even} rounding is used in cases where the 093 * decimal value must be rounded in order to meet the configuration requirements of the formatter 094 * instance. 095 * </p> 096 * 097 * @since 1.10.0 098 */ 099public 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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. If the 639 * number being formatted has an absolute value less than <code>10<sup>plainFormatMaxDecimalExponent + 1</sup></code> and greater than or equal to 640 * <code>10<sup>plainFormatMinDecimalExponent</sup></code> after any necessary rounding, then the formatted result will use the 641 * {@link DoubleFormat#PLAIN PLAIN} format type. Otherwise, {@link DoubleFormat#SCIENTIFIC SCIENTIFIC} format will be used. For example, if this value 642 * 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"}. 643 * 644 * <p> 645 * The default value is {@code 6}. 646 * 647 * <p> 648 * This value is ignored for formats other than {@link DoubleFormat#MIXED}. 649 * 650 * @param plainFormatMaxDecimalExponent maximum decimal exponent for values formatted as plain strings when using the {@link DoubleFormat#MIXED MIXED} 651 * format type. 652 * @return this instance 653 * @see #plainFormatMinDecimalExponent(int) 654 */ 655 public Builder plainFormatMaxDecimalExponent(final int plainFormatMaxDecimalExponent) { 656 this.plainFormatMaxDecimalExponent = plainFormatMaxDecimalExponent; 657 return this; 658 } 659 660 /** 661 * Sets the minimum decimal exponent for numbers formatted as plain decimal strings when using the {@link DoubleFormat#MIXED MIXED} format type. If the 662 * number being formatted has an absolute value less than <code>10<sup>plainFormatMaxDecimalExponent + 1</sup></code> and greater than or equal to 663 * <code>10<sup>plainFormatMinDecimalExponent</sup></code> after any necessary rounding, then the formatted result will use the 664 * {@link DoubleFormat#PLAIN PLAIN} format type. Otherwise, {@link DoubleFormat#SCIENTIFIC SCIENTIFIC} format will be used. For example, if this value 665 * 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"}. 666 * 667 * <p> 668 * The default value is {@code -3}. 669 * 670 * <p> 671 * This value is ignored for formats other than {@link DoubleFormat#MIXED}. 672 * 673 * @param plainFormatMinDecimalExponent maximum decimal exponent for values formatted as plain strings when using the {@link DoubleFormat#MIXED MIXED} 674 * format type. 675 * @return this instance 676 * @see #plainFormatMinDecimalExponent(int) 677 */ 678 public Builder plainFormatMinDecimalExponent(final int plainFormatMinDecimalExponent) { 679 this.plainFormatMinDecimalExponent = plainFormatMinDecimalExponent; 680 return this; 681 } 682 } 683 684 /** 685 * Format class that uses engineering notation for all values. 686 */ 687 private static final class EngineeringDoubleFormat extends AbstractDoubleFormat { 688 689 /** 690 * Constructs a new instance. 691 * 692 * @param builder builder instance containing configuration values 693 */ 694 EngineeringDoubleFormat(final Builder builder) { 695 super(builder); 696 } 697 698 /** {@inheritDoc} */ 699 @Override 700 public String applyFiniteInternal(final ParsedDecimal val) { 701 return val.toEngineeringString(this); 702 } 703 } 704 705 /** 706 * Format class producing results similar to {@link Double#toString()}, with plain decimal notation for small numbers relatively close to zero and 707 * scientific notation otherwise. 708 */ 709 private static final class MixedDoubleFormat extends AbstractDoubleFormat { 710 711 /** Max decimal exponent for plain format. */ 712 private final int plainMaxExponent; 713 714 /** Min decimal exponent for plain format. */ 715 private final int plainMinExponent; 716 717 /** 718 * Constructs a new instance. 719 * 720 * @param builder builder instance containing configuration values 721 */ 722 MixedDoubleFormat(final Builder builder) { 723 super(builder); 724 725 this.plainMaxExponent = builder.plainFormatMaxDecimalExponent; 726 this.plainMinExponent = builder.plainFormatMinDecimalExponent; 727 } 728 729 /** {@inheritDoc} */ 730 @Override 731 protected String applyFiniteInternal(final ParsedDecimal val) { 732 final int sciExp = val.getScientificExponent(); 733 if (sciExp <= plainMaxExponent && sciExp >= plainMinExponent) { 734 return val.toPlainString(this); 735 } 736 return val.toScientificString(this); 737 } 738 } 739 740 /** 741 * Format class that produces plain decimal strings that do not use scientific notation. 742 */ 743 private static final class PlainDoubleFormat extends AbstractDoubleFormat { 744 745 /** 746 * Constructs a new instance. 747 * 748 * @param builder builder instance containing configuration values 749 */ 750 PlainDoubleFormat(final Builder builder) { 751 super(builder); 752 } 753 754 /** 755 * {@inheritDoc} 756 */ 757 @Override 758 protected String applyFiniteInternal(final ParsedDecimal val) { 759 return val.toPlainString(this); 760 } 761 } 762 763 /** 764 * Format class that uses scientific notation for all values. 765 */ 766 private static final class ScientificDoubleFormat extends AbstractDoubleFormat { 767 768 /** 769 * Constructs a new instance. 770 * 771 * @param builder builder instance containing configuration values 772 */ 773 ScientificDoubleFormat(final Builder builder) { 774 super(builder); 775 } 776 777 /** {@inheritDoc} */ 778 @Override 779 public String applyFiniteInternal(final ParsedDecimal val) { 780 return val.toScientificString(this); 781 } 782 } 783 784 /** Function used to construct instances for this format type. */ 785 private final Function<Builder, DoubleFunction<String>> factory; 786 787 /** 788 * Constructs a new instance. 789 * 790 * @param factory function used to construct format instances 791 */ 792 DoubleFormat(final Function<Builder, DoubleFunction<String>> factory) { 793 this.factory = factory; 794 } 795 796 /** 797 * Creates a {@link Builder} for building formatter functions for this format type. 798 * 799 * @return builder instance 800 */ 801 public Builder builder() { 802 return new Builder(factory); 803 } 804}