001 /*
002 * Java GPX Library (jpx-3.1.0).
003 * Copyright (c) 2016-2023 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * 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 *
017 * Author:
018 * Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020 package io.jenetics.jpx.geom;
021
022 import static java.lang.Double.isInfinite;
023 import static java.lang.Double.isNaN;
024 import static java.util.Objects.requireNonNull;
025
026 import java.io.Serial;
027
028 /**
029 * This class implements the the
030 * <a href="http://en.wikipedia.org/wiki/Kahan_summation_algorithm">Kahan
031 * summation algorithm</a>, which significantly reduces the numerical error when
032 * adding double values.
033 *
034 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
035 * @since 1.0
036 * @version 1.0
037 */
038 final class DoubleAdder
039 extends Number
040 implements Comparable<DoubleAdder>
041 {
042 @Serial
043 private static final long serialVersionUID = 1L;
044
045 private double _sum = 0.0;
046 private double _simpleSum = 0.0;
047 private double _compensation = 0.0;
048
049 /**
050 * Create a new adder with the initial value of {@code 0.0}.
051 */
052
053 DoubleAdder() {
054 }
055
056 /**
057 * Reset the adder to the initial value of {@code 0.0}.
058 *
059 * @return {@code this} adder, for command chaining
060 */
061 private DoubleAdder reset() {
062 _sum = 0.0;
063 _simpleSum = 0.0;
064 _compensation = 0.0;
065 return this;
066 }
067
068 /**
069 * Set the adder to the given {@code value}.
070 *
071 * @param value the new adder value
072 * @return {@code this} adder, for command chaining
073 * @throws java.lang.NullPointerException if the given {@code value} is
074 * {@code null}
075 */
076 public DoubleAdder set(final DoubleAdder value) {
077 return reset().add(requireNonNull(value));
078 }
079
080 /**
081 * Add the given {@code value} to this adder, using the
082 * <a href="http://en.wikipedia.org/wiki/Kahan_summation_algorithm">Kahan
083 * summation algorithm</a>
084 *
085 * @param value the {@code value} to add
086 * @return {@code this} adder, for command chaining
087 */
088 public DoubleAdder add(final double value) {
089 addWithCompensation(value);
090 _simpleSum += value;
091 return this;
092 }
093
094 /**
095 * Add the given values to this adder.
096 *
097 * @param values the values to add.
098 * @return {@code this} adder, for command chaining
099 */
100 public DoubleAdder add(final double[] values) {
101 for (int i = values.length; --i >= 0;) {
102 add(values[i]);
103 }
104
105 return this;
106 }
107
108 private void addWithCompensation(final double value) {
109 final double y = value - _compensation;
110 final double t = _sum + y;
111 _compensation = (t - _sum) - y;
112 _sum = t;
113 }
114
115 /**
116 * Add the given {@code value} to this adder, using the
117 * <a href="http://en.wikipedia.org/wiki/Kahan_summation_algorithm">Kahan
118 * summation algorithm</a>
119 *
120 * @param value the {@code value} to add
121 * @return {@code this} adder, for command chaining
122 * @throws java.lang.NullPointerException if the given {@code value} is
123 * {@code null}
124 */
125 public DoubleAdder add(final DoubleAdder value) {
126 addWithCompensation(value._sum);
127 addWithCompensation(value._compensation);
128 _simpleSum += value._simpleSum;
129 return this;
130 }
131
132 /**
133 * Add the given {@code value} to this adder, using the
134 * <a href="http://en.wikipedia.org/wiki/Kahan_summation_algorithm">Kahan
135 * summation algorithm</a>
136 *
137 * @param other the {@code value} to add
138 * @return {@code this} adder, for command chaining
139 * @throws java.lang.NullPointerException if the given {@code value} is
140 * {@code null}
141 */
142 public DoubleAdder combine(final DoubleAdder other) {
143 return add(other);
144 }
145
146 public double value() {
147 final double result = _sum + _compensation;
148 return isNaN(result) && isInfinite(_simpleSum) ? _simpleSum : result;
149 }
150
151 @Override
152 public int intValue() {
153 return (int)value();
154 }
155
156 @Override
157 public long longValue() {
158 return (long)value();
159 }
160
161 @Override
162 public float floatValue() {
163 return (float)value();
164 }
165
166 @Override
167 public double doubleValue() {
168 return value();
169 }
170
171 @Override
172 public int compareTo(final DoubleAdder other) {
173 return Double.compare(doubleValue(), other.doubleValue());
174 }
175
176 @Override
177 public int hashCode() {
178 return Double.hashCode(doubleValue());
179 }
180
181 @Override
182 public boolean equals(final Object obj) {
183 return obj instanceof DoubleAdder &&
184 Double.compare(doubleValue(), ((DoubleAdder)obj).doubleValue()) == 0;
185 }
186
187 @Override
188 public String toString() {
189 return Double.toString(doubleValue());
190 }
191
192
193 /* *************************************************************************
194 * Some static helper methods.
195 **************************************************************************/
196
197 /**
198 * Return the sum of the given double array.
199 *
200 * @param values the values to sum up.
201 * @return the sum of the given {@code values}.
202 * @throws NullPointerException if the given array is {@code null}.
203 */
204 public static double sum(final double[] values) {
205 return new DoubleAdder().add(values).doubleValue();
206 }
207 }
|