1   /* Copyright 2022-2026 Thales Alenia Space
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS 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    *   http://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.orekit.utils.formatting;
18  
19  import org.hipparchus.util.FastMath;
20  import org.orekit.errors.OrekitInternalError;
21  
22  import java.io.IOException;
23  
24  /** Formatter for double numbers with low overhead.
25   * <p>
26   * This class is intended to be used when formatting large amounts of data with
27   * fixed formats like, for example, large ephemeris or measurement files.
28   * </p>
29   * <p>
30   * Building the formatter is done once, and the formatter
31   * {@link #appendTo(Appendable, double)} or {@link #toString(double)} methods can
32   * be called hundreds of thousands of times, without incurring the overhead that
33   * would occur with {@code String.format()}. Some tests showed this formatter is
34   * about 5 times faster than {@code String.format()} with
35   * {@code %{width}.{%precision}f} format.
36   * </p>
37   * <p>
38   * Instances of this class are immutable
39   * </p>
40   * @author Luc Maisonobe
41   * @since 14.0
42   */
43  public abstract class FastDoubleFormatter {
44  
45      /** Number of characters to output. */
46      private final int width;
47  
48      /** Simple constructor.
49       * <p>
50       * This constructor is equivalent to {@link java.util.Formatter Formatter}
51       * float format {@code %{width}.{precision}f}
52       * </p>
53       * @param width number of characters to output
54       */
55      protected FastDoubleFormatter(final int width) {
56          this.width = width;
57      }
58  
59      /** Get the width.
60       * @return width
61       */
62      public int getWidth() {
63          return width;
64      }
65  
66      /** Append one formatted value to an {@code Appendable}.
67       * @param appendable to append value to
68       * @param value value to format
69       * @exception IOException if an I/O error occurs
70       */
71      public void appendTo(final Appendable appendable, final double value) throws IOException {
72  
73          if (Double.isNaN(value)) {
74              // special case for NaN
75              for (int i = 0; i < width - 3; ++i) {
76                  appendable.append(' ');
77              }
78              appendable.append("NaN");
79          } else {
80  
81              if (Double.isInfinite(value)) {
82                  // special case for infinities
83                  if (FastMath.copySign(1.0, value) < 0) {
84                      for (int i = 0; i < width - 9; ++i) {
85                          appendable.append(' ');
86                      }
87                      appendable.append("-Infinity");
88                  } else {
89                      for (int i = 0; i < width - 8; ++i) {
90                          appendable.append(' ');
91                      }
92                      appendable.append("Infinity");
93                  }
94              } else {
95                  // regular number
96                  appendRegularValueTo(appendable, value);
97              }
98          }
99  
100     }
101 
102     /** Append one formatted value to an {@code Appendable}.
103      * @param appendable to append value to
104      * @param value value to format
105      * @exception IOException if an I/O error occurs
106      */
107     protected abstract void appendRegularValueTo(Appendable appendable, double value) throws IOException;
108 
109     /** Format one value.
110      * @param value value to format
111      * @return formatted string
112      */
113     public String toString(final double value) {
114         try {
115             final StringBuilder builder = new StringBuilder();
116             appendTo(builder, value);
117             return builder.toString();
118         } catch (IOException ioe) {
119             // this should never happen
120             throw new OrekitInternalError(ioe);
121         }
122     }
123 
124 }