[flang] Begin adding tests
authorpeter klausler <pklausler@nvidia.com>
Wed, 14 Aug 2019 22:25:16 +0000 (15:25 -0700)
committerpeter klausler <pklausler@nvidia.com>
Fri, 23 Aug 2019 18:31:20 +0000 (11:31 -0700)
Original-commit: flang-compiler/f18@8776d8b66334f8ba0886d074217f0c17cb6aa02b
Reviewed-on: https://github.com/flang-compiler/f18/pull/671
Tree-same-pre-rewrite: false

flang/lib/decimal/big-radix-floating-point.h
flang/lib/decimal/binary-to-decimal.cc
flang/lib/decimal/decimal-to-binary.cc
flang/lib/decimal/decimal.h
flang/test/CMakeLists.txt
flang/test/decimal/CMakeLists.txt [new file with mode: 0644]
flang/test/decimal/quick-sanity-test.cc [new file with mode: 0644]

index 3104b81..f4f5622 100644 (file)
@@ -43,10 +43,11 @@ static constexpr std::uint64_t TenToThe(int power) {
 // even, so that pairs of decimal digits do not straddle Digits.
 // So LOG10RADIX must be 16 or 6.
 template<int PREC, int LOG10RADIX = 16> class BigRadixFloatingPointNumber {
-private:
+public:
   using Real = BinaryFloatingPointNumber<PREC>;
-
   static constexpr int log10Radix{LOG10RADIX};
+
+private:
   static constexpr std::uint64_t uint64Radix{TenToThe(log10Radix)};
   static constexpr int minDigitBits{
       64 - common::LeadingZeroBitCount(uint64Radix)};
@@ -103,8 +104,8 @@ public:
 
 private:
   BigRadixFloatingPointNumber(const BigRadixFloatingPointNumber &that)
-    : digits_{that.digits_}, exponent_{that.exponent_}, isNegative_{
-                                                            that.isNegative_} {
+    : digits_{that.digits_}, exponent_{that.exponent_},
+      isNegative_{that.isNegative_}, rounding_{that.rounding_} {
     for (int j{0}; j < digits_; ++j) {
       digit_[j] = that.digit_[j];
     }
index 1348ffc..37b6ac9 100644 (file)
@@ -21,13 +21,14 @@ template<int PREC, int LOG10RADIX>
 BigRadixFloatingPointNumber<PREC, LOG10RADIX>::BigRadixFloatingPointNumber(
     BinaryFloatingPointNumber<PREC> x, enum FortranRounding rounding)
   : rounding_{rounding} {
-  if (x.IsNegative() < 0) {
-    isNegative_ = true;
-    x.Negate();
-  }
+  bool negative{x.IsNegative()};
   if (x.IsZero()) {
+    isNegative_ = negative;
     return;
   }
+  if (negative) {
+    x.Negate();
+  }
   int twoPow{x.UnbiasedExponent()};
   twoPow -= x.bits - 1;
   if (!x.implicitMSB) {
@@ -44,6 +45,7 @@ BigRadixFloatingPointNumber<PREC, LOG10RADIX>::BigRadixFloatingPointNumber(
   auto word{x.Fraction()};
   word <<= lshift;
   SetTo(word);
+  isNegative_ = negative;
 
   // The significand is now encoded in *this as an integer (D) and
   // decimal exponent (E):  x = D * 10.**E * 2.**twoPow
@@ -297,9 +299,9 @@ ConversionToDecimalResult ConvertToDecimal(char *buffer, size_t size, int flags,
   if (x.IsNaN()) {
     return {"NaN", 3, 0, Invalid};
   } else if (x.IsInfinite()) {
-    return {x.IsNegative() ? "-Inf" : "+Inf", 4, 0, Exact};
+    return {x.IsNegative() ? "-Inf" : (flags & AlwaysSign) ? "+Inf" : "Inf", 4,
+        0, Exact};
   } else {
-    using Binary = BinaryFloatingPointNumber<PREC>;
     using Big = BigRadixFloatingPointNumber<PREC>;
     Big number{x, rounding};
     if ((flags & Minimize) && !x.IsZero()) {
@@ -311,6 +313,7 @@ ConversionToDecimalResult ConvertToDecimal(char *buffer, size_t size, int flags,
       // the bounds of the range of decimal values that will map back to the
       // original binary value, and find a (not necessary unique) shortest
       // decimal sequence in that range.
+      using Binary = typename Big::Real;
       Binary less{x};
       --less.raw;
       Binary more{x};
index e33b3e3..872891a 100644 (file)
@@ -409,3 +409,26 @@ template ConversionToBinaryResult<112> ConvertToBinary<112>(
     const char *&, enum FortranRounding);
 
 }
+
+extern "C" {
+ConversionResultFlags ConvertDecimalToFloat(
+    const char **p, float *f, enum FortranRounding rounding) {
+  auto result{Fortran::decimal::ConvertToBinary<24>(*p, rounding)};
+  *f = *reinterpret_cast<const float *>(&result.binary);
+  return result.flags;
+}
+ConversionResultFlags ConvertDecimalToDouble(
+    const char **p, double *d, enum FortranRounding rounding) {
+  auto result{Fortran::decimal::ConvertToBinary<53>(*p, rounding)};
+  *d = *reinterpret_cast<const double *>(&result.binary);
+  return result.flags;
+}
+#if __x86_64__
+ConversionResultFlags ConvertDecimalToLongDouble(
+    const char **p, long double *ld, enum FortranRounding rounding) {
+  auto result{Fortran::decimal::ConvertToBinary<64>(*p, rounding)};
+  *ld = *reinterpret_cast<const long double *>(&result.binary);
+  return result.flags;
+}
+#endif
+}
index 5fcd87a..a6d7962 100644 (file)
@@ -67,22 +67,28 @@ enum DecimalConversionFlags {
 namespace Fortran::decimal {
 
 template<int PREC>
-ConversionToDecimalResult ConvertToDecimal(char *, size_t, int flags,
-    int digits, enum FortranRounding rounding,
-    BinaryFloatingPointNumber<PREC> x);
+ConversionToDecimalResult ConvertToDecimal(char *, size_t,
+    enum DecimalConversionFlags flags, int digits,
+    enum FortranRounding rounding, BinaryFloatingPointNumber<PREC> x);
 
 extern template ConversionToDecimalResult ConvertToDecimal<8>(char *, size_t,
-    int, int, enum FortranRounding, BinaryFloatingPointNumber<8>);
+    enum DecimalConversionFlags, int, enum FortranRounding,
+    BinaryFloatingPointNumber<8>);
 extern template ConversionToDecimalResult ConvertToDecimal<11>(char *, size_t,
-    int, int, enum FortranRounding, BinaryFloatingPointNumber<11>);
+    enum DecimalConversionFlags, int, enum FortranRounding,
+    BinaryFloatingPointNumber<11>);
 extern template ConversionToDecimalResult ConvertToDecimal<24>(char *, size_t,
-    int, int, enum FortranRounding, BinaryFloatingPointNumber<24>);
+    enum DecimalConversionFlags, int, enum FortranRounding,
+    BinaryFloatingPointNumber<24>);
 extern template ConversionToDecimalResult ConvertToDecimal<53>(char *, size_t,
-    int, int, enum FortranRounding, BinaryFloatingPointNumber<53>);
+    enum DecimalConversionFlags, int, enum FortranRounding,
+    BinaryFloatingPointNumber<53>);
 extern template ConversionToDecimalResult ConvertToDecimal<64>(char *, size_t,
-    int, int, enum FortranRounding, BinaryFloatingPointNumber<64>);
+    enum DecimalConversionFlags, int, enum FortranRounding,
+    BinaryFloatingPointNumber<64>);
 extern template ConversionToDecimalResult ConvertToDecimal<112>(char *, size_t,
-    int, int, enum FortranRounding, BinaryFloatingPointNumber<112>);
+    enum DecimalConversionFlags, int, enum FortranRounding,
+    BinaryFloatingPointNumber<112>);
 
 template<int PREC> struct ConversionToBinaryResult {
   BinaryFloatingPointNumber<PREC> binary;
@@ -105,7 +111,7 @@ extern template ConversionToBinaryResult<64> ConvertToBinary<64>(
     const char *&, enum FortranRounding = RoundNearest);
 extern template ConversionToBinaryResult<112> ConvertToBinary<112>(
     const char *&, enum FortranRounding = RoundNearest);
-}  // namespace
+}
 extern "C" {
 #endif /* C++ */
 
@@ -118,7 +124,16 @@ ConversionToDecimalResult ConvertLongDoubleToDecimal(
     char *, size_t, int flags, int digits, enum FortranRounding, long double);
 #endif
 
+ConversionResultFlags ConvertDecimalToFloat(
+    const char **, float *, enum FortranRounding);
+ConversionResultFlags ConvertDecimalToDouble(
+    const char **, double *, enum FortranRounding);
+#if __x86_64__
+ConversionResultFlags ConvertDecimalToLongDouble(
+    const char **, long double *, enum FortranRounding);
+#endif
+
 #ifdef __cplusplus
 }
 #endif /* C++ */
-#endif /* DECIMAL_H_ */
+#endif
index 5d6e9a4..a473c27 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright (c) 2018, NVIDIA CORPORATION.  All rights reserved.
+# Copyright (c) 2018-2019, NVIDIA CORPORATION.  All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -12,5 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+add_subdirectory(decimal)
 add_subdirectory(evaluate)
 add_subdirectory(semantics)
diff --git a/flang/test/decimal/CMakeLists.txt b/flang/test/decimal/CMakeLists.txt
new file mode 100644 (file)
index 0000000..0025ced
--- /dev/null
@@ -0,0 +1,39 @@
+# Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
+#
+# 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.
+
+add_executable(quick-sanity-test
+  quick-sanity-test.cc
+)
+
+target_link_libraries(quick-sanity-test
+  FortranDecimal
+)
+
+# add_executable(thorough-test
+#   thorough-test.cc
+# )
+
+# target_link_libraries(thorough-test
+#   FortranDecimal
+# )
+
+# add_executable(benchmark01
+#   benchmark01.cc
+# )
+
+# target_link_libraries(benchmark01
+#   FortranDecimal
+# )
+
+add_test(Sanity quick-sanity-test)
diff --git a/flang/test/decimal/quick-sanity-test.cc b/flang/test/decimal/quick-sanity-test.cc
new file mode 100644 (file)
index 0000000..99b52fe
--- /dev/null
@@ -0,0 +1,88 @@
+// Copyright (c) 2019, NVIDIA CORPORATION.  All rights reserved.
+//
+// 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.
+
+#include "../../lib/decimal/decimal.h"
+#include <cinttypes>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+
+static int tests{0};
+static int fails{0};
+
+std::ostream &failed(float x) {
+  ++fails;
+  return std::cout << "FAIL: 0x" << std::hex
+      << *reinterpret_cast<std::uint32_t *>(&x) << std::dec;
+}
+
+void testDirect(float x, const char *expect, int expectExpo) {
+  char buffer[1024];
+  ++tests;
+  auto result{ConvertFloatToDecimal(buffer, sizeof buffer,
+      static_cast<enum DecimalConversionFlags>(0), 1024,
+      RoundNearest, x)};
+  if (result.str == nullptr) {
+    failed(x) << ": no result str\n";
+  } else if (std::strcmp(result.str, expect) != 0) {
+    failed(x) << ": expect '" << expect << "', got '" << result.str << "'\n";
+  } else if (result.decimalExponent != expectExpo) {
+    failed(x) << ": expect exponent " << expectExpo << ", got "
+        << result.decimalExponent << '\n';
+  }
+}
+
+void testReadback(float x) {
+  char buffer[1024];
+  ++tests;
+  auto result{ConvertFloatToDecimal(buffer, sizeof buffer,
+      static_cast<enum DecimalConversionFlags>(0), 1024,
+      RoundNearest, x)};
+  if (result.str == nullptr) {
+    failed(x) << ": no result str\n";
+  } else {
+    float y{0};
+    char *q{const_cast<char *>(result.str)};
+    std::sprintf(q + result.length, "e%d", static_cast<int>(result.decimalExponent - result.length));
+    const char *p{q};
+    auto flags{ConvertDecimalToFloat(&p, &y, RoundNearest)};
+    if (x != y || *p != '\0' || (flags & Invalid)) {
+      failed(x) << ": -> '" << buffer << "' -> 0x"
+          << std::hex << *reinterpret_cast<std::uint32_t *>(&y)
+          << std::dec << " '" << p << "'\n";
+    }
+  }
+}
+
+int main() {
+  testDirect(-1.0, "-1", 1);
+  testDirect(0.0, "0", 0);
+  testDirect(1.0, "1", 1);
+  testDirect(2.0, "2", 1);
+  testDirect(-1.0, "-1", 1);
+  testDirect(314159, "314159", 6);
+  testDirect(0.0625, "625", -1);
+  float x;
+  std::uint32_t *ix{reinterpret_cast<std::uint32_t *>(&x)};
+  *ix = 0x80000000;
+  testDirect(x, "-0", 0);
+  *ix = 0x7f800000;
+  testDirect(x, "Inf", 0);
+  *ix = 0xff800000;
+  testDirect(x, "-Inf", 0);
+  *ix = 0xffffffff;
+  testDirect(x, "NaN", 0);
+  std::cout << tests << " tests run, " << fails << " tests failed\n";
+  return fails > 0;
+}