View Javadoc
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 }