Make assertThat(doubleArray).usingExactEquality accept expected values as Numbers as well as Doubles, with a runtime check on the type. Similarly for Floats.

Note that we don't support any doubles in the float case, because it's too hard to figure out whether any given double has an exact float representation. And we don't support BigInteger or BigDecimal in either case. Although we could figure it out there, it wouldn't be simple, and there doesn't seem much point: users won't have these things by mistake; if they have them on purpose then they are presumably expecting to include values which are big and/or high-precision; and anyway the usage rates of these are very low.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=145582571
diff --git a/core/src/main/java/com/google/common/truth/PrimitiveDoubleArraySubject.java b/core/src/main/java/com/google/common/truth/PrimitiveDoubleArraySubject.java
index 4d1df59..f38e677 100644
--- a/core/src/main/java/com/google/common/truth/PrimitiveDoubleArraySubject.java
+++ b/core/src/main/java/com/google/common/truth/PrimitiveDoubleArraySubject.java
@@ -16,6 +16,7 @@
 
 package com.google.common.truth;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.truth.Correspondence.tolerance;
 import static com.google.common.truth.DoubleSubject.checkTolerance;
@@ -346,12 +347,12 @@
         .comparingElementsUsing(tolerance(tolerance));
   }
 
-  private static final Correspondence<Double, Double> EXACT_EQUALITY_CORRESPONDENCE =
-      new Correspondence<Double, Double>() {
+  private static final Correspondence<Double, Number> EXACT_EQUALITY_CORRESPONDENCE =
+      new Correspondence<Double, Number>() {
 
         @Override
-        public boolean compare(Double actual, Double expected) {
-          return actual.equals(checkNotNull(expected));
+        public boolean compare(Double actual, Number expected) {
+          return actual.equals(checkedToDouble(expected));
         }
 
         @Override
@@ -360,6 +361,26 @@
         }
       };
 
+  private static double checkedToDouble(Number expected) {
+    checkNotNull(expected);
+    checkArgument(
+        expected instanceof Double
+            || expected instanceof Float
+            || expected instanceof Integer
+            || expected instanceof Long,
+        "Expected value in assertion using exact double equality was of unsupported type %s "
+            + "(it may not have an exact double representation)",
+        expected.getClass());
+    if (expected instanceof Long) {
+      checkArgument(
+          Math.abs((Long) expected) <= 1L << 53,
+          "Expected value %s in assertion using exact double equality was a long with an absolute "
+              + "value greater than 2^52 which has no exact double representation",
+          expected);
+    }
+    return expected.doubleValue();
+  }
+
   /**
    * Starts a method chain for a test proposition in which the actual values (i.e. the elements of
    * the array under test) are compared to expected elements using a {@link Correspondence} which
@@ -376,6 +397,12 @@
    * <pre>   {@code
    * assertThat(actualDoubleArray).usingExactEquality().contains(3.14159);}</pre>
    *
+   * <p>For convenience, some subsequent methods accept expected values as {@link Number} instances.
+   * These numbers must be either of type {@link Double}, {@link Float}, {@link Integer}, or
+   * {@link Long}, and if they are {@link Long} then their absolute values must not exceed 2^53
+   * which is just over 9e15. (This restriction ensures that the expected values have exact
+   * {@link Double} representations: using exact equality makes no sense if they do not.)
+   *
    * <ul>
    *   <li>It considers {@link Double#POSITIVE_INFINITY}, {@link Double#NEGATIVE_INFINITY}, and
    *       {@link Double#NaN} to be equal to themselves.
@@ -384,7 +411,7 @@
    *       expected {@link Double} instance is null.
    * </ul>
    */
-  public IterableSubject.UsingCorrespondence<Double, Double> usingExactEquality() {
+  public IterableSubject.UsingCorrespondence<Double, Number> usingExactEquality() {
     return new IterableSubject(failureStrategy, listRepresentation())
         .comparingElementsUsing(EXACT_EQUALITY_CORRESPONDENCE);
   }
diff --git a/core/src/main/java/com/google/common/truth/PrimitiveFloatArraySubject.java b/core/src/main/java/com/google/common/truth/PrimitiveFloatArraySubject.java
index 366aae4..2f9ca89 100644
--- a/core/src/main/java/com/google/common/truth/PrimitiveFloatArraySubject.java
+++ b/core/src/main/java/com/google/common/truth/PrimitiveFloatArraySubject.java
@@ -16,6 +16,7 @@
 
 package com.google.common.truth;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.truth.Correspondence.tolerance;
 import static com.google.common.truth.FloatSubject.checkTolerance;
@@ -346,12 +347,12 @@
         .comparingElementsUsing(tolerance(tolerance));
   }
 
-  private static final Correspondence<Float, Float> EXACT_EQUALITY_CORRESPONDENCE =
-      new Correspondence<Float, Float>() {
+  private static final Correspondence<Float, Number> EXACT_EQUALITY_CORRESPONDENCE =
+      new Correspondence<Float, Number>() {
 
         @Override
-        public boolean compare(Float actual, Float expected) {
-          return actual.equals(checkNotNull(expected));
+        public boolean compare(Float actual, Number expected) {
+          return actual.equals(checkedToFloat(expected));
         }
 
         @Override
@@ -360,6 +361,30 @@
         }
       };
 
+  private static float checkedToFloat(Number expected) {
+    checkNotNull(expected);
+    checkArgument(
+        expected instanceof Float || expected instanceof Integer || expected instanceof Long,
+        "Expected value in assertion using exact double equality was of unsupported type %s "
+            + "(it may not have an exact double representation)",
+        expected.getClass());
+    if (expected instanceof Integer) {
+      checkArgument(
+          Math.abs((Integer) expected) <= 1 << 24,
+          "Expected value %s in assertion using exact float equality was an int with an absolute "
+              + "value greater than 2^24 which has no exact float representation",
+          expected);
+    }
+    if (expected instanceof Long) {
+      checkArgument(
+          Math.abs((Long) expected) <= 1L << 24,
+          "Expected value %s in assertion using exact float equality was a long with an absolute "
+              + "value greater than 2^24 which has no exact float representation",
+          expected);
+    }
+    return expected.floatValue();
+  }
+
   /**
    * Starts a method chain for a test proposition in which the actual values (i.e. the elements of
    * the array under test) are compared to expected elements using a {@link Correspondence} which
@@ -376,6 +401,12 @@
    * <pre>   {@code
    * assertThat(actualFloatArray).usingExactEquality().contains(3.14159f);}</pre>
    *
+   * <p>For convenience, some subsequent methods accept expected values as {@link Number} instances.
+   * These numbers must be either of type {@link Float}, {@link Integer}, or {@link Long}, and if
+   * they are {@link Integer} or {@link Long} then their absolute values must not exceed 2^24 which
+   * is 16,777,216. (This restriction ensures that the expected values have exact {@link Float}
+   * representations: using exact equality makes no sense if they do not.)
+   *
    * <ul>
    *   <li>It considers {@link Float#POSITIVE_INFINITY}, {@link Float#NEGATIVE_INFINITY}, and
    *       {@link Float#NaN} to be equal to themselves.
@@ -384,7 +415,7 @@
    *       expected {@link Float} instance is null.
    * </ul>
    */
-  public IterableSubject.UsingCorrespondence<Float, Float> usingExactEquality() {
+  public IterableSubject.UsingCorrespondence<Float, Number> usingExactEquality() {
     return new IterableSubject(failureStrategy, listRepresentation())
         .comparingElementsUsing(EXACT_EQUALITY_CORRESPONDENCE);
   }
diff --git a/core/src/test/java/com/google/common/truth/PrimitiveDoubleArraySubjectTest.java b/core/src/test/java/com/google/common/truth/PrimitiveDoubleArraySubjectTest.java
index 3437dd7..b8cd29a 100644
--- a/core/src/test/java/com/google/common/truth/PrimitiveDoubleArraySubjectTest.java
+++ b/core/src/test/java/com/google/common/truth/PrimitiveDoubleArraySubjectTest.java
@@ -25,6 +25,8 @@
 import com.google.common.primitives.Doubles;
 import com.google.common.primitives.Floats;
 import com.google.common.primitives.Longs;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -1093,6 +1095,41 @@
   }
 
   @Test
+  public void usingTolerance_contains_otherTypes() {
+    // Expected value is Float
+    assertThat(array(1.0, 2.0 + 0.5 * DEFAULT_TOLERANCE, 3.0))
+        .usingTolerance(DEFAULT_TOLERANCE)
+        .contains(2.0f);
+    // Expected value is Integer
+    assertThat(array(1.0, 2.0 + 0.5 * DEFAULT_TOLERANCE, 3.0))
+        .usingTolerance(DEFAULT_TOLERANCE)
+        .contains(2);
+    // Expected value is Integer.MAX_VALUE
+    assertThat(array(1.0, Integer.MAX_VALUE + 0.5 * DEFAULT_TOLERANCE, 3.0))
+        .usingTolerance(DEFAULT_TOLERANCE)
+        .contains(Integer.MAX_VALUE);
+    // Expected value is Long
+    assertThat(array(1.0, 2.0 + 0.5 * DEFAULT_TOLERANCE, 3.0))
+        .usingTolerance(DEFAULT_TOLERANCE)
+        .contains(2L);
+    // Expected value is Long.MIN_VALUE. This is -1*2^63, which has an exact double representation.
+    // For the actual value we use the next value down, which is is 2^11 smaller (because the
+    // resolution of doubles with absolute values between 2^63 and 2^64 is 2^11). So we'll make the
+    // assertion with a tolerance of 2^12.
+    assertThat(array(1.0, nextAfter((double) Long.MIN_VALUE, NEGATIVE_INFINITY), 3.0))
+        .usingTolerance(1 << 12)
+        .contains(Long.MIN_VALUE);
+    // Expected value is BigInteger
+    assertThat(array(1.0, 2.0 + 0.5 * DEFAULT_TOLERANCE, 3.0))
+        .usingTolerance(DEFAULT_TOLERANCE)
+        .contains(BigInteger.valueOf(2));
+    // Expected value is BigDecimal
+    assertThat(array(1.0, 2.0 + 0.5 * DEFAULT_TOLERANCE, 3.0))
+        .usingTolerance(DEFAULT_TOLERANCE)
+        .contains(BigDecimal.valueOf(2.0));
+  }
+
+  @Test
   public void usingTolerance_contains_nullExpected() {
     try {
       assertThat(array(1.0, 2.0, 3.0)).usingTolerance(DEFAULT_TOLERANCE).contains(null);
@@ -1135,6 +1172,50 @@
   }
 
   @Test
+  public void usingExactEquality_contains_otherTypes() {
+    // Expected value is Float
+    assertThat(array(1.0, 2.0, 3.0)).usingExactEquality().contains(2.0f);
+    // Expected value is Integer
+    assertThat(array(1.0, 2.0, 3.0)).usingExactEquality().contains(2);
+    assertThat(array(1.0, Integer.MAX_VALUE, 3.0)).usingExactEquality().contains(Integer.MAX_VALUE);
+    // Expected value is Long - supported up to +/- 2^53
+    assertThat(array(1.0, 2.0, 3.0)).usingExactEquality().contains(2L);
+    assertThat(array(1.0, 1L << 53, 3.0)).usingExactEquality().contains(1L << 53);
+    try {
+      assertThat(array(1.0, 2.0, 3.0)).usingExactEquality().contains((1L << 53) + 1L);
+    } catch (IllegalArgumentException expected) {
+      assertThat(expected)
+          .hasMessageThat()
+          .isEqualTo(
+              "Expected value 9007199254740993 in assertion using exact double equality was a long "
+                  + "with an absolute value greater than 2^52 which has no exact double "
+                  + "representation");
+    }
+    // Expected value is BigInteger - not supported
+    try {
+      assertThat(array(1.0, 2.0, 3.0)).usingExactEquality().contains(BigInteger.valueOf(2));
+    } catch (IllegalArgumentException expected) {
+      assertThat(expected)
+          .hasMessageThat()
+          .isEqualTo(
+              "Expected value in assertion using exact double equality was of unsupported type "
+                  + BigInteger.class
+                  + " (it may not have an exact double representation)");
+    }
+    // Expected value is BigDecimal - not supported
+    try {
+      assertThat(array(1.0, 2.0, 3.0)).usingExactEquality().contains(BigDecimal.valueOf(2.0));
+    } catch (IllegalArgumentException expected) {
+      assertThat(expected)
+      .hasMessageThat()
+      .isEqualTo(
+          "Expected value in assertion using exact double equality was of unsupported type "
+              + BigDecimal.class
+              + " (it may not have an exact double representation)");
+    }
+  }
+
+  @Test
   public void usingExactEquality_contains_successWithInfinity() {
     assertThat(array(1.0, POSITIVE_INFINITY, 3.0)).usingExactEquality().contains(POSITIVE_INFINITY);
   }
diff --git a/core/src/test/java/com/google/common/truth/PrimitiveFloatArraySubjectTest.java b/core/src/test/java/com/google/common/truth/PrimitiveFloatArraySubjectTest.java
index 4ac9035..d639d10 100644
--- a/core/src/test/java/com/google/common/truth/PrimitiveFloatArraySubjectTest.java
+++ b/core/src/test/java/com/google/common/truth/PrimitiveFloatArraySubjectTest.java
@@ -25,6 +25,8 @@
 import com.google.common.primitives.Doubles;
 import com.google.common.primitives.Floats;
 import com.google.common.primitives.Longs;
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
@@ -1090,6 +1092,44 @@
   }
 
   @Test
+  public void usingTolerance_contains_otherTypes() {
+    // Expected value is Double
+    assertThat(array(1.0f, 2.0f + 0.5f * DEFAULT_TOLERANCE, 3.0f))
+        .usingTolerance(DEFAULT_TOLERANCE)
+        .contains(2.0);
+    // Expected value is Integer
+    assertThat(array(1.0f, 2.0f + 0.5f * DEFAULT_TOLERANCE, 3.0f))
+        .usingTolerance(DEFAULT_TOLERANCE)
+        .contains(2);
+    // Expected value is Integer.MIN_VALUE. This is -1*2^31, which has an exact float
+    // representation. For the actual value we use the next value down, which is 2^8 smaller
+    // (because the resolution of floats with absolute values between 2^31 and 2^32 is 2^8). So
+    // we'll make the assertion with a tolerance of 2^9.
+    assertThat(array(1.0f, Integer.MIN_VALUE + 0.5f * DEFAULT_TOLERANCE, 3.0f))
+        .usingTolerance(1 << 9)
+        .contains(Integer.MIN_VALUE);
+    // Expected value is Long
+    assertThat(array(1.0f, 2.0f + 0.5f * DEFAULT_TOLERANCE, 3.0f))
+        .usingTolerance(DEFAULT_TOLERANCE)
+        .contains(2L);
+    // Expected value is Long.MIN_VALUE. This is -1*2^63, which has an exact float representation.
+    // For the actual value we use the next value down, which is is 2^40 smaller (because the
+    // resolution of floats with absolute values between 2^63 and 2^64 is 2^40). So we'll make the
+    // assertion with a tolerance of 2^41.
+    assertThat(array(1.0f, nextAfter(Long.MIN_VALUE, NEGATIVE_INFINITY), 3.0f))
+        .usingTolerance(1L << 41)
+        .contains(Long.MIN_VALUE);
+    // Expected value is BigInteger
+    assertThat(array(1.0f, 2.0f + 0.5f * DEFAULT_TOLERANCE, 3.0f))
+        .usingTolerance(DEFAULT_TOLERANCE)
+        .contains(BigInteger.valueOf(2));
+    // Expected value is BigDecimal
+    assertThat(array(1.0f, 2.0f + 0.5f * DEFAULT_TOLERANCE, 3.0f))
+        .usingTolerance(DEFAULT_TOLERANCE)
+        .contains(BigDecimal.valueOf(2.0));
+  }
+
+  @Test
   public void usingTolerance_contains_nullExpected() {
     try {
       assertThat(array(1.0f, 2.0f, 3.0f)).usingTolerance(DEFAULT_TOLERANCE).contains(null);
@@ -1132,6 +1172,67 @@
   }
 
   @Test
+  public void usingExactEquality_contains_otherTypes() {
+    // Expected value is Integer - supported up to +/- 2^24
+    assertThat(array(1.0f, 2.0f, 3.0f)).usingExactEquality().contains(2);
+    assertThat(array(1.0f, 1 << 24, 3.0f)).usingExactEquality().contains(1 << 24);
+    try {
+      assertThat(array(1.0f, 2.0f, 3.0f)).usingExactEquality().contains((1 << 24) + 1);
+    } catch (IllegalArgumentException expected) {
+      assertThat(expected)
+          .hasMessageThat()
+          .isEqualTo(
+              "Expected value 16777217 in assertion using exact float equality was an int with an "
+                  + "absolute value greater than 2^24 which has no exact float representation");
+    }
+    // Expected value is Long - supported up to +/- 2^24
+    assertThat(array(1.0f, 2.0f, 3.0f)).usingExactEquality().contains(2L);
+    assertThat(array(1.0f, 1 << 24, 3.0f)).usingExactEquality().contains(1L << 24);
+    try {
+      assertThat(array(1.0f, 2.0f, 3.0f)).usingExactEquality().contains((1L << 24) + 1L);
+    } catch (IllegalArgumentException expected) {
+      assertThat(expected)
+          .hasMessageThat()
+          .isEqualTo(
+              "Expected value 16777217 in assertion using exact float equality was a long with an "
+                  + "absolute value greater than 2^24 which has no exact float representation");
+    }
+    // Expected value is Double - not supported
+    try {
+      assertThat(array(1.0f, 2.0f, 3.0f)).usingExactEquality().contains(2.0);
+    } catch (IllegalArgumentException expected) {
+      assertThat(expected)
+          .hasMessageThat()
+          .isEqualTo(
+              "Expected value in assertion using exact double equality was of unsupported type "
+                  + Double.class
+                  + " (it may not have an exact double representation)");
+    }
+    // Expected value is BigInteger - not supported
+    try {
+      assertThat(array(1.0f, 2.0f, 3.0f)).usingExactEquality().contains(BigInteger.valueOf(2));
+    } catch (IllegalArgumentException expected) {
+      assertThat(expected)
+          .hasMessageThat()
+          .isEqualTo(
+              "Expected value in assertion using exact double equality was of unsupported type "
+                  + BigInteger.class
+                  + " (it may not have an exact double representation)");
+    }
+    // Expected value is BigDecimal - not supported
+    try {
+      assertThat(array(1.0f, 2.0f, 3.0f)).usingExactEquality().contains(BigDecimal.valueOf(2.0));
+    } catch (IllegalArgumentException expected) {
+      assertThat(expected)
+      .hasMessageThat()
+      .isEqualTo(
+          "Expected value in assertion using exact double equality was of unsupported type "
+              + BigDecimal.class
+              + " (it may not have an exact double representation)");
+    }
+  }
+
+  @Test
   public void usingExactEquality_contains_successWithInfinity() {
     assertThat(array(1.0f, POSITIVE_INFINITY, 3.0f))
         .usingExactEquality()