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 }