// 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)};
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];
}
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) {
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
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()) {
// 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};
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
+}
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;
const char *&, enum FortranRounding = RoundNearest);
extern template ConversionToBinaryResult<112> ConvertToBinary<112>(
const char *&, enum FortranRounding = RoundNearest);
-} // namespace
+}
extern "C" {
#endif /* C++ */
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
-# 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.
# See the License for the specific language governing permissions and
# limitations under the License.
+add_subdirectory(decimal)
add_subdirectory(evaluate)
add_subdirectory(semantics)
--- /dev/null
+# 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)
--- /dev/null
+// 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;
+}