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