1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF 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 * https://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.apache.commons.statistics.examples.jmh.descriptive;
18
19 import java.math.BigInteger;
20 import java.nio.ByteBuffer;
21 import org.apache.commons.numbers.core.DD;
22
23 /**
24 * A mutable 128-bit signed integer.
25 *
26 * <p>This is a copy of {@code o.a.c.statistics.descriptive.Int128} to allow benchmarking.
27 * Additional methods may have been added for comparative benchmarks.
28 *
29 * <p>This is a specialised class to implement an accumulator of {@code long} values.
30 *
31 * <p>Note: This number uses a signed long integer representation of:
32 *
33 * <pre>value = 2<sup>64</sup> * hi64 + lo64</pre>
34 *
35 * <p>If the high value is zero then the low value is the long representation of the
36 * number including the sign bit. Otherwise the low value corresponds to a correction
37 * term for the scaled high value which contains the sign-bit of the number.
38 *
39 * @since 1.1
40 */
41 final class Int128 {
42 /** Mask for the lower 32-bits of a long. */
43 private static final long MASK32 = 0xffff_ffffL;
44
45 /** low 64-bits. */
46 private long lo;
47 /** high 64-bits. */
48 private long hi;
49
50 /**
51 * Create an instance.
52 */
53 private Int128() {
54 // No-op
55 }
56
57 /**
58 * Create an instance.
59 *
60 * @param x Value.
61 */
62 private Int128(long x) {
63 lo = x;
64 }
65
66 /**
67 * Create an instance using a direct binary representation.
68 * This is package-private for testing.
69 *
70 * @param hi High 64-bits.
71 * @param lo Low 64-bits.
72 */
73 Int128(long hi, long lo) {
74 this.lo = lo;
75 this.hi = hi;
76 }
77
78 /**
79 * Create an instance. The initial value is zero.
80 *
81 * @return the instance
82 */
83 static Int128 create() {
84 return new Int128();
85 }
86
87 /**
88 * Create an instance of the {@code long} value.
89 *
90 * @param x Value.
91 * @return the instance
92 */
93 static Int128 of(long x) {
94 return new Int128(x);
95 }
96
97 /**
98 * Adds the value.
99 *
100 * @param x Value.
101 */
102 void add(long x) {
103 final long y = lo;
104 final long r = y + x;
105 // Overflow if the result has the opposite sign of both arguments
106 // (+,+) -> -
107 // (-,-) -> +
108 // Detect opposite sign:
109 if (((y ^ r) & (x ^ r)) < 0) {
110 // Carry overflow bit
111 hi += x < 0 ? -1 : 1;
112 }
113 lo = r;
114 }
115
116 /**
117 * Adds the value.
118 *
119 * @param x Value.
120 */
121 void add2(long x) {
122 final long y = lo;
123 final long r = y + x;
124 // Overflow if the result has the opposite sign of both arguments
125 // (+,+) -> -
126 // (-,-) -> +
127 // Branchless.
128 // Extract sign bit.
129 final long signMask = ((y ^ r) & (x ^ r)) >> 63;
130 // Carry using [0/1] * [+1/-1]
131 hi += signMask & (1 - ((x >>> 62) & 0x2));
132 lo = r;
133 }
134
135 /**
136 * Adds the value.
137 *
138 * @param x Value.
139 */
140 void add(Int128 x) {
141 // Avoid issues adding to itself
142 final long l = x.lo;
143 final long h = x.hi;
144 add(l);
145 hi += h;
146 }
147
148 /**
149 * Compute the square of the low 64-bits of this number.
150 *
151 * <p>Warning: This ignores the upper 64-bits. Use with caution.
152 *
153 * @return the square
154 */
155 UInt128 squareLow() {
156 final long x = lo;
157 final long upper = IntMath.squareHigh(x);
158 return new UInt128(upper, x * x);
159 }
160
161 /**
162 * Compute the square of this number.
163 *
164 * @return the square
165 */
166 BigInteger square() {
167 if (hi == 0) {
168 final long x = lo;
169 final long upper = IntMath.squareHigh(x);
170 final long lower = x * x;
171 if (upper == 0) {
172 return BigInteger.valueOf(lower);
173 }
174 return new BigInteger(1, ByteBuffer.allocate(Long.BYTES * 2)
175 .putLong(upper).putLong(lower).array());
176 }
177 final BigInteger result = toBigInteger();
178 return result.multiply(result);
179 }
180
181 /**
182 * Convert to a BigInteger.
183 *
184 * @return the value
185 */
186 BigInteger toBigInteger() {
187 long h = hi;
188 long l = lo;
189 // Special cases
190 if (h == 0) {
191 return BigInteger.valueOf(l);
192 }
193 if (l == 0) {
194 return BigInteger.valueOf(h).shiftLeft(64);
195 }
196
197 // The representation is 2^64 * hi64 + lo64.
198 // Here we avoid evaluating the addition:
199 // BigInteger.valueOf(l).add(BigInteger.valueOf(h).shiftLeft(64))
200 // It is faster to create from bytes.
201 // BigInteger bytes are an unsigned integer in BigEndian format, plus a sign.
202 // If both values are positive we can use the values unchanged.
203 // Otherwise selective negation is used to create a positive magnitude
204 // and we track the sign.
205 // Note: Negation of -2^63 is valid to create an unsigned 2^63.
206
207 int sign = 1;
208 if ((h ^ l) < 0) {
209 // Opposite signs and lo64 is not zero.
210 // The lo64 bits are an adjustment to the magnitude of hi64
211 // to make it smaller.
212 // Here we rearrange to [2^64 * (hi64-1)] + [2^64 - lo64].
213 // The second term [2^64 - lo64] can use lo64 as an unsigned 64-bit integer.
214 // The first term [2^64 * (hi64-1)] does not work if low is zero.
215 // It would work if zero was detected and we carried the overflow
216 // bit up to h to make it equal to: (h - 1) + 1 == h.
217 // Instead lo64 == 0 is handled as a special case above.
218
219 if (h >= 0) {
220 // Treat (unchanged) low as an unsigned add
221 h = h - 1;
222 } else {
223 // As above with negation
224 h = ~h; // -h - 1
225 l = -l;
226 sign = -1;
227 }
228 } else if (h < 0) {
229 // Invert negative values to create the equivalent positive magnitude.
230 h = -h;
231 l = -l;
232 sign = -1;
233 }
234
235 return new BigInteger(sign,
236 ByteBuffer.allocate(Long.BYTES * 2)
237 .putLong(h).putLong(l).array());
238 }
239
240 /**
241 * Convert to a double-double.
242 *
243 * @return the value
244 */
245 DD toDD() {
246 // Don't combine two 64-bit DD numbers:
247 // DD.of(hi).scalb(64).add(DD.of(lo))
248 // It is more accurate to create a 96-bit number and add the final 32-bits.
249 // Sum low to high.
250 return DD.of(lo).add((hi & MASK32) * 0x1.0p64).add((hi >> Integer.SIZE) * 0x1.0p96);
251 }
252
253 /**
254 * Return the lower 64-bits as a {@code long} value.
255 *
256 * <p>If the high value is zero then the low value is the long representation of the
257 * number including the sign bit. Otherwise this value corresponds to a correction
258 * term for the scaled high value which contains the sign-bit of the number
259 * (see {@link Int128}).
260 *
261 * @return the low 64-bits
262 */
263 long lo64() {
264 return lo;
265 }
266
267 /**
268 * Return the higher 64-bits as a {@code long} value.
269 *
270 * @return the high 64-bits
271 * @see #lo64()
272 */
273 long hi64() {
274 return hi;
275 }
276 }