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