diff --git a/flang/lib/evaluate/real.h b/flang/lib/evaluate/real.h index bc60b8d8f673..bf69a9fea76c 100644 --- a/flang/lib/evaluate/real.h +++ b/flang/lib/evaluate/real.h @@ -86,6 +86,13 @@ public: constexpr bool IsNotANumber() const { return Exponent() == maxExponent && !GetSignificand().IsZero(); } + constexpr bool IsQuietNaN() const { + return Exponent() == maxExponent && + GetSignificand().BTEST(significandBits - 1); + } + constexpr bool IsSignalingNaN() const { + return IsNotANumber() && !GetSignificand().BTEST(significandBits - 1); + } constexpr bool IsInfinite() const { return Exponent() == maxExponent && GetSignificand().IsZero(); } @@ -223,7 +230,9 @@ public: ValueWithRealFlags result; if (IsNotANumber() || y.IsNotANumber()) { result.value.word_ = NaNWord(); // NaN + x -> NaN - result.flags.set(RealFlag::InvalidArgument); + if (IsSignalingNaN() || y.IsSignalingNaN()) { + result.flags.set(RealFlag::InvalidArgument); + } return result; } bool isNegative{IsNegative()}; @@ -511,7 +520,11 @@ private: if (negative) { word_ = word_.IBSET(bits - 1); } - return {RealFlag::Overflow}; + RealFlags flags{RealFlag::Overflow}; + if (!fraction.IsZero()) { + flags.set(RealFlag::Inexact); + } + return flags; } if (fraction.BTEST(fraction.bits - 1)) { // fraction is normalized diff --git a/flang/test/evaluate/fp-testing.cc b/flang/test/evaluate/fp-testing.cc index fa450f4dbe8e..a4cbc9e74585 100644 --- a/flang/test/evaluate/fp-testing.cc +++ b/flang/test/evaluate/fp-testing.cc @@ -19,7 +19,8 @@ using Fortran::evaluate::RealFlag; -ScopedHostFloatingPointEnvironment::ScopedHostFloatingPointEnvironment(bool treatDenormalOperandsAsZero, bool flushDenormalResultsToZero) { +ScopedHostFloatingPointEnvironment::ScopedHostFloatingPointEnvironment( + bool treatDenormalOperandsAsZero, bool flushDenormalResultsToZero) { errno = 0; if (feholdexcept(&originalFenv_) != 0) { std::fprintf(stderr, "feholdexcept() failed: %s\n", std::strerror(errno)); @@ -41,7 +42,7 @@ ScopedHostFloatingPointEnvironment::ScopedHostFloatingPointEnvironment(bool trea currentFenv_.__mxcsr &= ~0x8000; } #else - // TODO others + // TODO others #endif errno = 0; if (fesetenv(¤tFenv_) != 0) { @@ -58,6 +59,10 @@ ScopedHostFloatingPointEnvironment::~ScopedHostFloatingPointEnvironment() { } } +void ScopedHostFloatingPointEnvironment::ClearFlags() const { + feclearexcept(FE_ALL_EXCEPT); +} + RealFlags ScopedHostFloatingPointEnvironment::CurrentFlags() const { int exceptions = fetestexcept(FE_ALL_EXCEPT); RealFlags flags; diff --git a/flang/test/evaluate/fp-testing.h b/flang/test/evaluate/fp-testing.h index 5b04428eff60..8ee53ad17943 100644 --- a/flang/test/evaluate/fp-testing.h +++ b/flang/test/evaluate/fp-testing.h @@ -23,9 +23,11 @@ using Fortran::evaluate::RealFlags; class ScopedHostFloatingPointEnvironment { public: ScopedHostFloatingPointEnvironment(bool treatDenormalOperandsAsZero = false, - bool flushDenormalResultsToZero = false); + bool flushDenormalResultsToZero = false); ~ScopedHostFloatingPointEnvironment(); + void ClearFlags() const; RealFlags CurrentFlags() const; + private: fenv_t originalFenv_; fenv_t currentFenv_; diff --git a/flang/test/evaluate/real.cc b/flang/test/evaluate/real.cc index 8d97ff4435bd..b93e7931aa4d 100644 --- a/flang/test/evaluate/real.cc +++ b/flang/test/evaluate/real.cc @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "../../lib/evaluate/integer.h" #include "../../lib/evaluate/real.h" -#include "testing.h" #include "fp-testing.h" +#include "testing.h" +#include "../../lib/evaluate/integer.h" #include using namespace Fortran::evaluate; @@ -23,8 +23,8 @@ using namespace Fortran::evaluate; template void tests() { char desc[64]; using Word = typename R::Word; - std::snprintf(desc, sizeof desc, "bits=%d, le=%d", - R::bits, Word::littleEndian); + std::snprintf( + desc, sizeof desc, "bits=%d, le=%d", R::bits, Word::littleEndian); R zero; TEST(!zero.IsNegative())(desc); TEST(!zero.IsNotANumber())(desc); @@ -51,7 +51,10 @@ template void tests() { ValueWithRealFlags vr; MATCH(0, vr.value.RawBits().ToUInt64())(desc); TEST(vr.flags.empty())(desc); - R nan{Word{std::uint64_t{1}}.SHIFTL(R::bits).SubtractSigned(Word{std::uint64_t{1}}).value}; + R nan{Word{std::uint64_t{1}} + .SHIFTL(R::bits) + .SubtractSigned(Word{std::uint64_t{1}}) + .value}; MATCH(R::bits, nan.RawBits().POPCNT())(desc); TEST(!nan.IsNegative())(desc); TEST(nan.IsNotANumber())(desc); @@ -71,7 +74,8 @@ template void tests() { TEST(!inf.IsNotANumber())(desc); TEST(inf.IsInfinite())(desc); TEST(!inf.IsZero())(desc); - TEST(inf.RawBits().CompareUnsigned(inf.ABS().RawBits()) == Ordering::Equal)(desc); + TEST(inf.RawBits().CompareUnsigned(inf.ABS().RawBits()) == Ordering::Equal) + (desc); TEST(zero.Compare(inf) == Relation::Less)(desc); TEST(minusZero.Compare(inf) == Relation::Less)(desc); TEST(nan.Compare(inf) == Relation::Unordered)(desc); @@ -81,9 +85,14 @@ template void tests() { TEST(!negInf.IsNotANumber())(desc); TEST(negInf.IsInfinite())(desc); TEST(!negInf.IsZero())(desc); - TEST(inf.RawBits().CompareUnsigned(negInf.ABS().RawBits()) == Ordering::Equal)(desc); - TEST(inf.RawBits().CompareUnsigned(negInf.Negate().RawBits()) == Ordering::Equal)(desc); - TEST(inf.Negate().RawBits().CompareUnsigned(negInf.RawBits()) == Ordering::Equal)(desc); + TEST(inf.RawBits().CompareUnsigned(negInf.ABS().RawBits()) == Ordering::Equal) + (desc); + TEST(inf.RawBits().CompareUnsigned(negInf.Negate().RawBits()) == + Ordering::Equal) + (desc); + TEST(inf.Negate().RawBits().CompareUnsigned(negInf.RawBits()) == + Ordering::Equal) + (desc); TEST(zero.Compare(negInf) == Relation::Greater)(desc); TEST(minusZero.Compare(negInf) == Relation::Greater)(desc); TEST(nan.Compare(negInf) == Relation::Unordered)(desc); @@ -93,46 +102,48 @@ template void tests() { std::uint64_t x{1}; x <<= j; Integer<64> ix{x}; - TEST(!ix.IsNegative())("%s,%d,0x%llx",desc,j,x); - MATCH(x, ix.ToUInt64())("%s,%d,0x%llx",desc,j,x); + TEST(!ix.IsNegative())("%s,%d,0x%llx", desc, j, x); + MATCH(x, ix.ToUInt64())("%s,%d,0x%llx", desc, j, x); vr = R::ConvertSigned(ix); - TEST(!vr.value.IsNegative())("%s,%d,0x%llx",desc,j,x); - TEST(!vr.value.IsNotANumber())("%s,%d,0x%llx",desc,j,x); - TEST(!vr.value.IsZero())("%s,%d,0x%llx",desc,j,x); + TEST(!vr.value.IsNegative())("%s,%d,0x%llx", desc, j, x); + TEST(!vr.value.IsNotANumber())("%s,%d,0x%llx", desc, j, x); + TEST(!vr.value.IsZero())("%s,%d,0x%llx", desc, j, x); auto ivf = vr.value.template ToInteger>(); if (j > (maxExponent / 2)) { TEST(vr.flags.test(RealFlag::Overflow))(desc); - TEST(vr.value.IsInfinite())("%s,%d,0x%llx",desc,j,x); - TEST(ivf.flags.test(RealFlag::Overflow))("%s,%d,0x%llx",desc,j,x); - MATCH(0x7fffffffffffffff, ivf.value.ToUInt64())("%s,%d,0x%llx",desc,j,x); + TEST(vr.value.IsInfinite())("%s,%d,0x%llx", desc, j, x); + TEST(ivf.flags.test(RealFlag::Overflow))("%s,%d,0x%llx", desc, j, x); + MATCH(0x7fffffffffffffff, ivf.value.ToUInt64()) + ("%s,%d,0x%llx", desc, j, x); } else { TEST(vr.flags.empty())(desc); - TEST(!vr.value.IsInfinite())("%s,%d,0x%llx",desc,j,x); - TEST(ivf.flags.empty())("%s,%d,0x%llx",desc,j,x); - MATCH(x, ivf.value.ToUInt64())("%s,%d,0x%llx",desc,j,x); + TEST(!vr.value.IsInfinite())("%s,%d,0x%llx", desc, j, x); + TEST(ivf.flags.empty())("%s,%d,0x%llx", desc, j, x); + MATCH(x, ivf.value.ToUInt64())("%s,%d,0x%llx", desc, j, x); } ix = ix.Negate().value; - TEST(ix.IsNegative())("%s,%d,0x%llx",desc,j,x); + TEST(ix.IsNegative())("%s,%d,0x%llx", desc, j, x); x = -x; std::int64_t nx = x; - MATCH(x, ix.ToUInt64())("%s,%d,0x%llx",desc,j,x); - MATCH(nx, ix.ToInt64())("%s,%d,0x%llx",desc,j,x); + MATCH(x, ix.ToUInt64())("%s,%d,0x%llx", desc, j, x); + MATCH(nx, ix.ToInt64())("%s,%d,0x%llx", desc, j, x); vr = R::ConvertSigned(ix); - TEST(vr.value.IsNegative())("%s,%d,0x%llx",desc,j,x); - TEST(!vr.value.IsNotANumber())("%s,%d,0x%llx",desc,j,x); - TEST(!vr.value.IsZero())("%s,%d,0x%llx",desc,j,x); + TEST(vr.value.IsNegative())("%s,%d,0x%llx", desc, j, x); + TEST(!vr.value.IsNotANumber())("%s,%d,0x%llx", desc, j, x); + TEST(!vr.value.IsZero())("%s,%d,0x%llx", desc, j, x); ivf = vr.value.template ToInteger>(); if (j > (maxExponent / 2)) { TEST(vr.flags.test(RealFlag::Overflow))(desc); - TEST(vr.value.IsInfinite())("%s,%d,0x%llx",desc,j,x); - TEST(ivf.flags.test(RealFlag::Overflow))("%s,%d,0x%llx",desc,j,x); - MATCH(0x8000000000000000, ivf.value.ToUInt64())("%s,%d,0x%llx",desc,j,x); + TEST(vr.value.IsInfinite())("%s,%d,0x%llx", desc, j, x); + TEST(ivf.flags.test(RealFlag::Overflow))("%s,%d,0x%llx", desc, j, x); + MATCH(0x8000000000000000, ivf.value.ToUInt64()) + ("%s,%d,0x%llx", desc, j, x); } else { TEST(vr.flags.empty())(desc); - TEST(!vr.value.IsInfinite())("%s,%d,0x%llx",desc,j,x); - TEST(ivf.flags.empty())("%s,%d,0x%llx",desc,j,x); - MATCH(x, ivf.value.ToUInt64())("%s,%d,0x%llx",desc,j,x); - MATCH(nx, ivf.value.ToInt64())("%s,%d,0x%llx",desc,j,x); + TEST(!vr.value.IsInfinite())("%s,%d,0x%llx", desc, j, x); + TEST(ivf.flags.empty())("%s,%d,0x%llx", desc, j, x); + MATCH(x, ivf.value.ToUInt64())("%s,%d,0x%llx", desc, j, x); + MATCH(nx, ivf.value.ToInt64())("%s,%d,0x%llx", desc, j, x); } } } @@ -146,18 +157,38 @@ std::uint32_t MakeReal(std::uint32_t n) { } std::uint32_t NormalizeNaN(std::uint32_t x) { - if ((x & 0x7f800000) == 0x7f800000 && - (x & 0x007fffff) != 0) { + if ((x & 0x7f800000) == 0x7f800000 && (x & 0x007fffff) != 0) { x = 0x7fe00000; } return x; } +std::uint32_t FlagsToBits(const RealFlags &flags) { + std::uint32_t bits{0}; + if (flags.test(RealFlag::Overflow)) { + bits |= 1; + } + if (flags.test(RealFlag::DivideByZero)) { + bits |= 2; + } + if (flags.test(RealFlag::InvalidArgument)) { + bits |= 4; + } + if (flags.test(RealFlag::Underflow)) { + bits |= 8; + } + if (flags.test(RealFlag::Inexact)) { + bits |= 0x10; + } + return bits; +} + void subset32bit() { union { std::uint32_t u32; float f; } u; + ScopedHostFloatingPointEnvironment fpenv; for (std::uint32_t j{0}; j < 8192; ++j) { std::uint32_t rj{MakeReal(j)}; u.u32 = rj; @@ -168,24 +199,27 @@ void subset32bit() { u.u32 = rk; float fk{u.f}; RealKind4 y{Integer<32>{std::uint64_t{rk}}}; - { ValueWithRealFlags sum{x.Add(y)}; - ScopedHostFloatingPointEnvironment fpenv; + { + ValueWithRealFlags sum{x.Add(y)}; + fpenv.ClearFlags(); float fcheck{fj + fk}; + auto actualFlags{FlagsToBits(fpenv.CurrentFlags())}; u.f = fcheck; std::uint32_t rcheck{NormalizeNaN(u.u32)}; std::uint32_t check = sum.value.RawBits().ToUInt64(); MATCH(rcheck, check)("0x%x + 0x%x", rj, rk); + MATCH(actualFlags, FlagsToBits(sum.flags))("0x%x + 0x%x", rj, rk); } - { ValueWithRealFlags diff{x.Subtract(y)}; - ScopedHostFloatingPointEnvironment fpenv; + { + ValueWithRealFlags diff{x.Subtract(y)}; float fcheck{fj - fk}; u.f = fcheck; std::uint32_t rcheck{NormalizeNaN(u.u32)}; std::uint32_t check = diff.value.RawBits().ToUInt64(); MATCH(rcheck, check)("0x%x - 0x%x", rj, rk); } - { ValueWithRealFlags prod{x.Multiply(y)}; - ScopedHostFloatingPointEnvironment fpenv; + { + ValueWithRealFlags prod{x.Multiply(y)}; float fcheck{fj * fk}; u.f = fcheck; std::uint32_t rcheck{NormalizeNaN(u.u32)}; @@ -194,7 +228,6 @@ void subset32bit() { } #if 0 { ValueWithRealFlags quot{x.Divide(y)}; - ScopedHostFloatingPointEnvironment fpenv; float fcheck{fj * fk}; u.f = fcheck; std::uint32_t rcheck{NormalizeNaN(u.u32)}; @@ -212,6 +245,6 @@ int main() { tests(); tests(); tests(); - subset32bit(); // TODO rounding modes, exception flags + subset32bit(); // TODO rounding modes return testing::Complete(); } diff --git a/flang/test/evaluate/testing.cc b/flang/test/evaluate/testing.cc index d66c4d71d1a8..2eb8147bb247 100644 --- a/flang/test/evaluate/testing.cc +++ b/flang/test/evaluate/testing.cc @@ -90,8 +90,8 @@ FailureDetailPrinter Compare(const char *file, int line, const char *xs, return BitBucket; } else { ++failures; - fprintf(stderr, "%s:%d: FAIL: %s[0x%llx] %s %s[0x%llx]\n", file, line, xs, x, - rel, ys, y); + fprintf(stderr, "%s:%d: FAIL: %s[0x%llx] %s %s[0x%llx]\n", file, line, xs, + x, rel, ys, y); return PrintFailureDetails; } }