aboutsummaryrefslogtreecommitdiffstats
path: root/guava/src/com/google/common/math/DoubleMath.java
blob: ded38b26572e5cffb78d423ba0c94392f9249f20 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
/*
 * Copyright (C) 2011 The Guava Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.common.math;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.math.DoubleUtils.IMPLICIT_BIT;
import static com.google.common.math.DoubleUtils.SIGNIFICAND_BITS;
import static com.google.common.math.DoubleUtils.getSignificand;
import static com.google.common.math.DoubleUtils.isFinite;
import static com.google.common.math.DoubleUtils.isNormal;
import static com.google.common.math.DoubleUtils.scaleNormalize;
import static com.google.common.math.MathPreconditions.checkInRange;
import static com.google.common.math.MathPreconditions.checkNonNegative;
import static com.google.common.math.MathPreconditions.checkRoundingUnnecessary;
import static java.lang.Math.abs;
import static java.lang.Math.copySign;
import static java.lang.Math.getExponent;
import static java.lang.Math.log;
import static java.lang.Math.rint;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.primitives.Booleans;

import java.math.BigInteger;
import java.math.RoundingMode;

/**
 * A class for arithmetic on doubles that is not covered by {@link java.lang.Math}.
 *
 * @author Louis Wasserman
 * @since 11.0
 */
public final class DoubleMath {
  /*
   * This method returns a value y such that rounding y DOWN (towards zero) gives the same result
   * as rounding x according to the specified mode.
   */
  static double roundIntermediate(double x, RoundingMode mode) {
    if (!isFinite(x)) {
      throw new ArithmeticException("input is infinite or NaN");
    }
    switch (mode) {
      case UNNECESSARY:
        checkRoundingUnnecessary(isMathematicalInteger(x));
        return x;

      case FLOOR:
        if (x >= 0.0 || isMathematicalInteger(x)) {
          return x;
        } else {
          return x - 1.0;
        }

      case CEILING:
        if (x <= 0.0 || isMathematicalInteger(x)) {
          return x;
        } else {
          return x + 1.0;
        }

      case DOWN:
        return x;

      case UP:
        if (isMathematicalInteger(x)) {
          return x;
        } else {
          return x + Math.copySign(1.0, x);
        }

      case HALF_EVEN:
        return rint(x);

      case HALF_UP: {
        double z = rint(x);
        if (abs(x - z) == 0.5) {
          return x + copySign(0.5, x);
        } else {
          return z;
        }
      }

      case HALF_DOWN: {
        double z = rint(x);
        if (abs(x - z) == 0.5) {
          return x;
        } else {
          return z;
        }
      }

      default:
        throw new AssertionError();
    }
  }

  /**
   * Returns the {@code int} value that is equal to {@code x} rounded with the specified rounding
   * mode, if possible.
   *
   * @throws ArithmeticException if
   *         <ul>
   *         <li>{@code x} is infinite or NaN
   *         <li>{@code x}, after being rounded to a mathematical integer using the specified
   *         rounding mode, is either less than {@code Integer.MIN_VALUE} or greater than {@code
   *         Integer.MAX_VALUE}
   *         <li>{@code x} is not a mathematical integer and {@code mode} is
   *         {@link RoundingMode#UNNECESSARY}
   *         </ul>
   */
  public static int roundToInt(double x, RoundingMode mode) {
    double z = roundIntermediate(x, mode);
    checkInRange(z > MIN_INT_AS_DOUBLE - 1.0 & z < MAX_INT_AS_DOUBLE + 1.0);
    return (int) z;
  }

  private static final double MIN_INT_AS_DOUBLE = -0x1p31;
  private static final double MAX_INT_AS_DOUBLE = 0x1p31 - 1.0;

  /**
   * Returns the {@code long} value that is equal to {@code x} rounded with the specified rounding
   * mode, if possible.
   *
   * @throws ArithmeticException if
   *         <ul>
   *         <li>{@code x} is infinite or NaN
   *         <li>{@code x}, after being rounded to a mathematical integer using the specified
   *         rounding mode, is either less than {@code Long.MIN_VALUE} or greater than {@code
   *         Long.MAX_VALUE}
   *         <li>{@code x} is not a mathematical integer and {@code mode} is
   *         {@link RoundingMode#UNNECESSARY}
   *         </ul>
   */
  public static long roundToLong(double x, RoundingMode mode) {
    double z = roundIntermediate(x, mode);
    checkInRange(MIN_LONG_AS_DOUBLE - z < 1.0 & z < MAX_LONG_AS_DOUBLE_PLUS_ONE);
    return (long) z;
  }

  private static final double MIN_LONG_AS_DOUBLE = -0x1p63;
  /*
   * We cannot store Long.MAX_VALUE as a double without losing precision.  Instead, we store
   * Long.MAX_VALUE + 1 == -Long.MIN_VALUE, and then offset all comparisons by 1.
   */
  private static final double MAX_LONG_AS_DOUBLE_PLUS_ONE = 0x1p63;

  /**
   * Returns the {@code BigInteger} value that is equal to {@code x} rounded with the specified
   * rounding mode, if possible.
   *
   * @throws ArithmeticException if
   *         <ul>
   *         <li>{@code x} is infinite or NaN
   *         <li>{@code x} is not a mathematical integer and {@code mode} is
   *         {@link RoundingMode#UNNECESSARY}
   *         </ul>
   */
  public static BigInteger roundToBigInteger(double x, RoundingMode mode) {
    x = roundIntermediate(x, mode);
    if (MIN_LONG_AS_DOUBLE - x < 1.0 & x < MAX_LONG_AS_DOUBLE_PLUS_ONE) {
      return BigInteger.valueOf((long) x);
    }
    int exponent = getExponent(x);
    long significand = getSignificand(x);
    BigInteger result = BigInteger.valueOf(significand).shiftLeft(exponent - SIGNIFICAND_BITS);
    return (x < 0) ? result.negate() : result;
  }

  /**
   * Returns {@code true} if {@code x} is exactly equal to {@code 2^k} for some finite integer
   * {@code k}.
   */
  public static boolean isPowerOfTwo(double x) {
    return x > 0.0 && isFinite(x) && LongMath.isPowerOfTwo(getSignificand(x));
  }

  /**
   * Returns the base 2 logarithm of a double value.
   *
   * <p>Special cases:
   * <ul>
   * <li>If {@code x} is NaN or less than zero, the result is NaN.
   * <li>If {@code x} is positive infinity, the result is positive infinity.
   * <li>If {@code x} is positive or negative zero, the result is negative infinity.
   * </ul>
   *
   * <p>The computed result is within 1 ulp of the exact result.
   *
   * <p>If the result of this method will be immediately rounded to an {@code int},
   * {@link #log2(double, RoundingMode)} is faster.
   */
  public static double log2(double x) {
    return log(x) / LN_2; // surprisingly within 1 ulp according to tests
  }

  private static final double LN_2 = log(2);

  /**
   * Returns the base 2 logarithm of a double value, rounded with the specified rounding mode to an
   * {@code int}.
   *
   * <p>Regardless of the rounding mode, this is faster than {@code (int) log2(x)}.
   *
   * @throws IllegalArgumentException if {@code x <= 0.0}, {@code x} is NaN, or {@code x} is
   *         infinite
   */
  @SuppressWarnings("fallthrough")
  public static int log2(double x, RoundingMode mode) {
    checkArgument(x > 0.0 && isFinite(x), "x must be positive and finite");
    int exponent = getExponent(x);
    if (!isNormal(x)) {
      return log2(x * IMPLICIT_BIT, mode) - SIGNIFICAND_BITS;
      // Do the calculation on a normal value.
    }
    // x is positive, finite, and normal
    boolean increment;
    switch (mode) {
      case UNNECESSARY:
        checkRoundingUnnecessary(isPowerOfTwo(x));
        // fall through
      case FLOOR:
        increment = false;
        break;
      case CEILING:
        increment = !isPowerOfTwo(x);
        break;
      case DOWN:
        increment = exponent < 0 & !isPowerOfTwo(x);
        break;
      case UP:
        increment = exponent >= 0 & !isPowerOfTwo(x);
        break;
      case HALF_DOWN:
      case HALF_EVEN:
      case HALF_UP:
        double xScaled = scaleNormalize(x);
        // sqrt(2) is irrational, and the spec is relative to the "exact numerical result,"
        // so log2(x) is never exactly exponent + 0.5.
        increment = (xScaled * xScaled) > 2.0;
        break;
      default:
        throw new AssertionError();
    }
    return increment ? exponent + 1 : exponent;
  }

  /**
   * Returns {@code true} if {@code x} represents a mathematical integer.
   *
   * <p>This is equivalent to, but not necessarily implemented as, the expression {@code
   * !Double.isNaN(x) && !Double.isInfinite(x) && x == Math.rint(x)}.
   */
  public static boolean isMathematicalInteger(double x) {
    return isFinite(x)
        && (x == 0.0 ||
            SIGNIFICAND_BITS - Long.numberOfTrailingZeros(getSignificand(x)) <= getExponent(x));
  }

  /**
   * Returns {@code n!}, that is, the product of the first {@code n} positive
   * integers, {@code 1} if {@code n == 0}, or e n!}, or
   * {@link Double#POSITIVE_INFINITY} if {@code n! > Double.MAX_VALUE}.
   *
   * <p>The result is within 1 ulp of the true value.
   *
   * @throws IllegalArgumentException if {@code n < 0}
   */
  public static double factorial(int n) {
    checkNonNegative("n", n);
    if (n > MAX_FACTORIAL) {
      return Double.POSITIVE_INFINITY;
    } else {
      // Multiplying the last (n & 0xf) values into their own accumulator gives a more accurate
      // result than multiplying by everySixteenthFactorial[n >> 4] directly.
      double accum = 1.0;
      for (int i = 1 + (n & ~0xf); i <= n; i++) {
        accum *= i;
      }
      return accum * everySixteenthFactorial[n >> 4];
    }
  }

  @VisibleForTesting
  static final int MAX_FACTORIAL = 170;

  @VisibleForTesting
  static final double[] everySixteenthFactorial = {
      0x1.0p0,
      0x1.30777758p44,
      0x1.956ad0aae33a4p117,
      0x1.ee69a78d72cb6p202,
      0x1.fe478ee34844ap295,
      0x1.c619094edabffp394,
      0x1.3638dd7bd6347p498,
      0x1.7cac197cfe503p605,
      0x1.1e5dfc140e1e5p716,
      0x1.8ce85fadb707ep829,
      0x1.95d5f3d928edep945};

  /**
   * Returns {@code true} if {@code a} and {@code b} are within {@code tolerance} of each other.
   *
   * <p>Technically speaking, this is equivalent to
   * {@code Math.abs(a - b) <= tolerance || Double.valueOf(a).equals(Double.valueOf(b))}.
   *
   * <p>Notable special cases include:
   * <ul>
   * <li>All NaNs are fuzzily equal.
   * <li>If {@code a == b}, then {@code a} and {@code b} are always fuzzily equal.
   * <li>Positive and negative zero are always fuzzily equal.
   * <li>If {@code tolerance} is zero, and neither {@code a} nor {@code b} is NaN, then
   * {@code a} and {@code b} are fuzzily equal if and only if {@code a == b}.
   * <li>With {@link Double#POSITIVE_INFINITY} tolerance, all non-NaN values are fuzzily equal.
   * <li>With finite tolerance, {@code Double.POSITIVE_INFINITY} and {@code
   * Double.NEGATIVE_INFINITY} are fuzzily equal only to themselves.
   * </li>
   *
   * <p>This is reflexive and symmetric, but <em>not</em> transitive, so it is <em>not</em> an
   * equivalence relation and <em>not</em> suitable for use in {@link Object#equals}
   * implementations.
   *
   * @throws IllegalArgumentException if {@code tolerance} is {@code < 0} or NaN
   * @since 13.0
   */
  public static boolean fuzzyEquals(double a, double b, double tolerance) {
    MathPreconditions.checkNonNegative("tolerance", tolerance);
    return
          Math.copySign(a - b, 1.0) <= tolerance
           // copySign(x, 1.0) is a branch-free version of abs(x), but with different NaN semantics
          || (a == b) // needed to ensure that infinities equal themselves
          || ((a != a) && (b != b)); // x != x is equivalent to Double.isNaN(x), but faster
  }

  /**
   * Compares {@code a} and {@code b} "fuzzily," with a tolerance for nearly-equal values.
   *
   * <p>This method is equivalent to
   * {@code fuzzyEquals(a, b, tolerance) ? 0 : Double.compare(a, b)}. In particular, like
   * {@link Double#compare(double, double)}, it treats all NaN values as equal and greater than all
   * other values (including {@link Double#POSITIVE_INFINITY}).
   *
   * <p>This is <em>not</em> a total ordering and is <em>not</em> suitable for use in
   * {@link Comparable#compareTo} implementations.  In particular, it is not transitive.
   *
   * @throws IllegalArgumentException if {@code tolerance} is {@code < 0} or NaN
   * @since 13.0
   */
  public static int fuzzyCompare(double a, double b, double tolerance) {
    if (fuzzyEquals(a, b, tolerance)) {
      return 0;
    } else if (a < b) {
      return -1;
    } else if (a > b) {
      return 1;
    } else {
      return Booleans.compare(Double.isNaN(a), Double.isNaN(b));
    }
  }

  private DoubleMath() {}
}