diff --git a/flang/lib/decimal/big-radix-floating-point.h b/flang/lib/decimal/big-radix-floating-point.h index 3104b8140e19..f4f5622cb6c2 100644 --- a/flang/lib/decimal/big-radix-floating-point.h +++ b/flang/lib/decimal/big-radix-floating-point.h @@ -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 class BigRadixFloatingPointNumber { -private: +public: using Real = BinaryFloatingPointNumber; - 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]; } diff --git a/flang/lib/decimal/binary-to-decimal.cc b/flang/lib/decimal/binary-to-decimal.cc index 1348ffc74d0a..37b6ac9fa095 100644 --- a/flang/lib/decimal/binary-to-decimal.cc +++ b/flang/lib/decimal/binary-to-decimal.cc @@ -21,13 +21,14 @@ template BigRadixFloatingPointNumber::BigRadixFloatingPointNumber( BinaryFloatingPointNumber 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::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; using Big = BigRadixFloatingPointNumber; 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}; diff --git a/flang/lib/decimal/decimal-to-binary.cc b/flang/lib/decimal/decimal-to-binary.cc index e33b3e3b35af..872891ae9eab 100644 --- a/flang/lib/decimal/decimal-to-binary.cc +++ b/flang/lib/decimal/decimal-to-binary.cc @@ -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(&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(&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(&result.binary); + return result.flags; +} +#endif +} diff --git a/flang/lib/decimal/decimal.h b/flang/lib/decimal/decimal.h index 5fcd87a6bd79..a6d79627625e 100644 --- a/flang/lib/decimal/decimal.h +++ b/flang/lib/decimal/decimal.h @@ -67,22 +67,28 @@ enum DecimalConversionFlags { namespace Fortran::decimal { template -ConversionToDecimalResult ConvertToDecimal(char *, size_t, int flags, - int digits, enum FortranRounding rounding, - BinaryFloatingPointNumber x); +ConversionToDecimalResult ConvertToDecimal(char *, size_t, + enum DecimalConversionFlags flags, int digits, + enum FortranRounding rounding, BinaryFloatingPointNumber 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 struct ConversionToBinaryResult { BinaryFloatingPointNumber 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 diff --git a/flang/test/CMakeLists.txt b/flang/test/CMakeLists.txt index 5d6e9a48260d..a473c27a2705 100644 --- a/flang/test/CMakeLists.txt +++ b/flang/test/CMakeLists.txt @@ -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 index 000000000000..0025cedc2463 --- /dev/null +++ b/flang/test/decimal/CMakeLists.txt @@ -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 index 000000000000..99b52fe0337e --- /dev/null +++ b/flang/test/decimal/quick-sanity-test.cc @@ -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 +#include +#include +#include + +static int tests{0}; +static int fails{0}; + +std::ostream &failed(float x) { + ++fails; + return std::cout << "FAIL: 0x" << std::hex + << *reinterpret_cast(&x) << std::dec; +} + +void testDirect(float x, const char *expect, int expectExpo) { + char buffer[1024]; + ++tests; + auto result{ConvertFloatToDecimal(buffer, sizeof buffer, + static_cast(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(0), 1024, + RoundNearest, x)}; + if (result.str == nullptr) { + failed(x) << ": no result str\n"; + } else { + float y{0}; + char *q{const_cast(result.str)}; + std::sprintf(q + result.length, "e%d", static_cast(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(&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(&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; +}