ART: Simplify unstarted-runtime ceil and floor

Just use the C functions like libcore does. Add tests.

Bug: 28132336
Change-Id: Ic5f469e8504c9f2b7756280558fd1610ed8015ba
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index 80ffedc..02e05c5 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -524,31 +524,14 @@
   }
 }
 
-static double ComputeCeil(double in) {
-  double out;
-  // Special cases:
-  // 1) NaN, infinity, +0, -0 -> out := in. All are guaranteed by cmath.
-  // -1 < in < 0 -> out := -0.
-  if (-1.0 < in && in < 0) {
-    out = -0.0;
-  } else {
-    out = ceil(in);
-  }
-  return out;
-}
-
 void UnstartedRuntime::UnstartedMathCeil(
     Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
-  double in = shadow_frame->GetVRegDouble(arg_offset);
-  result->SetD(ComputeCeil(in));
+  result->SetD(ceil(shadow_frame->GetVRegDouble(arg_offset)));
 }
 
 void UnstartedRuntime::UnstartedMathFloor(
     Thread* self ATTRIBUTE_UNUSED, ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
-  double in = shadow_frame->GetVRegDouble(arg_offset);
-  // From the JavaDocs:
-  // "Note that the value of Math.ceil(x) is exactly the value of -Math.floor(-x)."
-  result->SetD(-ComputeCeil(-in));
+  result->SetD(floor(shadow_frame->GetVRegDouble(arg_offset)));
 }
 
 void UnstartedRuntime::UnstartedObjectHashCode(
diff --git a/runtime/interpreter/unstarted_runtime_test.cc b/runtime/interpreter/unstarted_runtime_test.cc
index f40e4e3..1b5b665 100644
--- a/runtime/interpreter/unstarted_runtime_test.cc
+++ b/runtime/interpreter/unstarted_runtime_test.cc
@@ -16,6 +16,9 @@
 
 #include "unstarted_runtime.h"
 
+#include <limits>
+
+#include "base/casts.h"
 #include "class_linker.h"
 #include "common_runtime_test.h"
 #include "dex_instruction.h"
@@ -154,6 +157,31 @@
                  length);
     CheckObjectArray(dst_handle.Get(), expected_result);
   }
+
+  void TestCeilFloor(bool ceil,
+                     Thread* self,
+                     ShadowFrame* tmp,
+                     double const test_pairs[][2],
+                     size_t num_pairs)
+      SHARED_REQUIRES(Locks::mutator_lock_) {
+    for (size_t i = 0; i < num_pairs; ++i) {
+      tmp->SetVRegDouble(0, test_pairs[i][0]);
+
+      JValue result;
+      if (ceil) {
+        UnstartedMathCeil(self, tmp, &result, 0);
+      } else {
+        UnstartedMathFloor(self, tmp, &result, 0);
+      }
+
+      ASSERT_FALSE(self->IsExceptionPending());
+
+      // We want precise results.
+      int64_t result_int64t = bit_cast<int64_t, double>(result.GetD());
+      int64_t expect_int64t = bit_cast<int64_t, double>(test_pairs[i][1]);
+      EXPECT_EQ(expect_int64t, result_int64t) << result.GetD() << " vs " << test_pairs[i][1];
+    }
+  }
 };
 
 TEST_F(UnstartedRuntimeTest, MemoryPeekByte) {
@@ -603,5 +631,63 @@
   ShadowFrame::DeleteDeoptimizedFrame(tmp);
 }
 
+TEST_F(UnstartedRuntimeTest, Ceil) {
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  constexpr double nan = std::numeric_limits<double>::quiet_NaN();
+  constexpr double inf = std::numeric_limits<double>::infinity();
+  constexpr double ld1 = static_cast<double>((UINT64_C(1) << 53) - 1);
+  constexpr double ld2 = static_cast<double>(UINT64_C(1) << 55);
+  constexpr double test_pairs[][2] = {
+      { -0.0, -0.0 },
+      {  0.0,  0.0 },
+      { -0.5, -0.0 },
+      { -1.0, -1.0 },
+      {  0.5,  1.0 },
+      {  1.0,  1.0 },
+      {  nan,  nan },
+      {  inf,  inf },
+      { -inf, -inf },
+      {  ld1,  ld1 },
+      {  ld2,  ld2 }
+  };
+
+  TestCeilFloor(true /* ceil */, self, tmp, test_pairs, arraysize(test_pairs));
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
+TEST_F(UnstartedRuntimeTest, Floor) {
+  Thread* self = Thread::Current();
+  ScopedObjectAccess soa(self);
+
+  ShadowFrame* tmp = ShadowFrame::CreateDeoptimizedFrame(10, nullptr, nullptr, 0);
+
+  constexpr double nan = std::numeric_limits<double>::quiet_NaN();
+  constexpr double inf = std::numeric_limits<double>::infinity();
+  constexpr double ld1 = static_cast<double>((UINT64_C(1) << 53) - 1);
+  constexpr double ld2 = static_cast<double>(UINT64_C(1) << 55);
+  constexpr double test_pairs[][2] = {
+      { -0.0, -0.0 },
+      {  0.0,  0.0 },
+      { -0.5, -1.0 },
+      { -1.0, -1.0 },
+      {  0.5,  0.0 },
+      {  1.0,  1.0 },
+      {  nan,  nan },
+      {  inf,  inf },
+      { -inf, -inf },
+      {  ld1,  ld1 },
+      {  ld2,  ld2 }
+  };
+
+  TestCeilFloor(false /* floor */, self, tmp, test_pairs, arraysize(test_pairs));
+
+  ShadowFrame::DeleteDeoptimizedFrame(tmp);
+}
+
 }  // namespace interpreter
 }  // namespace art