Added test for floating point conversions
am: 170e00af50

Change-Id: If86d06eb0bc0f89c493ed88967460ec071f72913
diff --git a/Android.mk b/Android.mk
index 8220ee0..b78e9e1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_MODULE := cr
 LOCAL_MODULE_TAGS := optional
-LOCAL_SDK_VERSION := 8
+LOCAL_SDK_VERSION := 19
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
diff --git a/src/com/hp/creals/CR.java b/src/com/hp/creals/CR.java
index cce2a92..5312b15 100644
--- a/src/com/hp/creals/CR.java
+++ b/src/com/hp/creals/CR.java
@@ -714,6 +714,8 @@
 /**
 * Return a double which differs by less than one in the least
 * represented bit from the constructive real.
+* (We're in fact closer to round-to-nearest than that, but we can't and
+* don't promise correct rounding.)
 */
     public double doubleValue() {
         int my_msd = iter_msd(-1080 /* slightly > exp. range */);
@@ -725,7 +727,9 @@
         long exp_adj = may_underflow? needed_prec + 96 : needed_prec;
         long orig_exp = (scaled_int_rep >> 52) & 0x7ff;
         if (((orig_exp + exp_adj) & ~0x7ff) != 0) {
-            // overflow
+            // Original unbiased exponent is > 50. Exp_adj > -1050.
+            // Thus this can overflow the 11 bit exponent only if the result
+            // itself overflows.
             if (scaled_int < 0.0) {
                 return Double.NEGATIVE_INFINITY;
             } else {
@@ -748,6 +752,8 @@
 */
     public float floatValue() {
         return (float)doubleValue();
+        // Note that double-rounding is not a problem here, since we
+        // cannot, and do not, guarantee correct rounding.
     }
 
 /**
diff --git a/tests/Android.mk b/tests/Android.mk
index 59a564a..441752e 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -21,7 +21,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_PACKAGE_NAME := CRTests
-LOCAL_SDK_VERSION := 8
+LOCAL_SDK_VERSION := 19
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-java-files-under, ../src)
 # Empirically, LOCAL_INSTRUMENTATION_FOR doesn't work, perhaps because it
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 48e1c98..4bc08c9 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -25,7 +25,7 @@
     android:versionName="1.0">
 
     <uses-sdk
-        android:minSdkVersion="8" />
+        android:minSdkVersion="19" />
 
     <instrumentation
         android:name="android.test.InstrumentationTestRunner"
diff --git a/tests/README.txt b/tests/README.txt
index a940b2c..6cedac3 100644
--- a/tests/README.txt
+++ b/tests/README.txt
@@ -1,7 +1,7 @@
 Run on Android with
 
 1) Build the tests.
-2) adb install <tree root>/out/target/product/generic/data/app/CRTests/CRTests.apk
+2) adb install <tree root>/out/target/product/<name>/data/app/CRTests/CRTests.apk
 3) adb shell am instrument -w com.hp.creals.tests/android.test.InstrumentationTestRunner
 
 The last step takes around 10 minutes on a Nexus 5.
diff --git a/tests/src/com/hp/creals/ConversionTest.java b/tests/src/com/hp/creals/ConversionTest.java
new file mode 100644
index 0000000..90844b6
--- /dev/null
+++ b/tests/src/com/hp/creals/ConversionTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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.hp.creals;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import java.util.Random;
+
+public class ConversionTest extends TestCase {
+    private static void check(boolean x, String s) {
+        if (!x) throw new AssertionFailedError(s);
+    }
+    final static int NRANDOM = 100;    // Number of random values to
+                                       // test.  Bigger ==> slower
+    final CR BIG = CR.valueOf(Double.MAX_VALUE);
+    final CR HUGE = BIG.multiply(BIG);
+    final CR TINY = CR.ONE.shiftRight(1078);
+
+    public void checkDoubleConversion(double x) {
+        if (!Double.isNaN(x) && !Double.isInfinite(x)) {
+            CR crValue = CR.valueOf(x);
+            check(x == crValue.doubleValue(), "double conversion: " + x);
+        }
+    }
+
+    public void checkFloatConversion(float f) {
+        if (!Float.isNaN(f) && !Float.isInfinite(f)) {
+            CR crValue = CR.valueOf(f);
+            check(f == crValue.floatValue(), "float conversion: " + f);
+        }
+    }
+
+    public void checkNearbyConversions(double x) {
+        checkDoubleConversion(x);
+        checkDoubleConversion(Math.nextAfter(x, Double.NEGATIVE_INFINITY));
+        checkDoubleConversion(Math.nextAfter(x, Double.POSITIVE_INFINITY));
+        float f = (float)x;
+        checkFloatConversion(f);
+        checkFloatConversion(Math.nextAfter(f, Double.NEGATIVE_INFINITY));
+        checkFloatConversion(Math.nextAfter(f, Double.POSITIVE_INFINITY));
+    }
+
+    public void testConversions() {
+        check(TINY.doubleValue() == 0.0d, "Tiny.doubleValue()");
+        checkNearbyConversions(0.0d);
+        checkNearbyConversions(Double.MAX_VALUE);
+        checkNearbyConversions(Double.MIN_VALUE);
+        check(HUGE.doubleValue() == Double.POSITIVE_INFINITY,
+                "double +infinity");
+        check(HUGE.negate().doubleValue() == Double.NEGATIVE_INFINITY,
+                "double -infinity");
+        check(HUGE.floatValue() == Float.POSITIVE_INFINITY,
+                "float +infinity");
+        check(HUGE.negate().floatValue() == Float.NEGATIVE_INFINITY,
+                "float -infinity");
+        for (double x = 1.0d; x != 0.0d; x /= 2.0d) {
+            checkNearbyConversions(x);
+        }
+        for (double x = -1.0d; x != Double.NEGATIVE_INFINITY; x *= 2.0d) {
+            checkNearbyConversions(x);
+        }
+        Random r = new Random();  // Random seed!
+        for (int i = 0; i < NRANDOM; ++i) {
+            double d = Math.exp(1000.0 * r.nextDouble());
+            if (r.nextBoolean()) d = -d;
+            checkNearbyConversions(d);
+        }
+    }
+}