diff --git a/flang/lib/Decimal/big-radix-floating-point.h b/flang/lib/Decimal/big-radix-floating-point.h index 7d5d31b7788d7..2143d1d9b3f77 100644 --- a/flang/lib/Decimal/big-radix-floating-point.h +++ b/flang/lib/Decimal/big-radix-floating-point.h @@ -369,6 +369,12 @@ template class BigRadixFloatingPointNumber { } return result; } + constexpr Raw HUGE() const { + Raw result{static_cast(Real::maxExponent)}; + result <<= Real::significandBits; + result |= SignBit(); + return result - 1; // decrement exponent, set all significand bits + } Digit digit_[maxDigits]; // in little-endian order: digit_[0] is LSD int digits_{0}; // # of elements in digit_[] array; zero when zero diff --git a/flang/lib/Decimal/decimal-to-binary.cpp b/flang/lib/Decimal/decimal-to-binary.cpp index 780979f747f5b..05a5f038353e0 100644 --- a/flang/lib/Decimal/decimal-to-binary.cpp +++ b/flang/lib/Decimal/decimal-to-binary.cpp @@ -237,6 +237,15 @@ template class IntermediateFloat { int exponent_{0}; }; +// The standard says that these overflow cases round to "representable" +// numbers, and some popular compilers interpret that to mean +/-HUGE() +// rather than +/-Inf. +static inline constexpr bool RoundOverflowToHuge( + enum FortranRounding rounding, bool isNegative) { + return rounding == RoundToZero || (!isNegative && rounding == RoundDown) || + (isNegative && rounding == RoundUp); +} + template ConversionToBinaryResult IntermediateFloat::ToBinary( bool isNegative, FortranRounding rounding) const { @@ -260,7 +269,7 @@ ConversionToBinaryResult IntermediateFloat::ToBinary( if (guard <= oneHalf) { if ((!isNegative && rounding == RoundUp) || (isNegative && rounding == RoundDown)) { - // round to minimum nonzero value + // round to least nonzero value } else { // round to zero if (guard != 0) { flags |= Underflow; @@ -310,12 +319,17 @@ ConversionToBinaryResult IntermediateFloat::ToBinary( } else if (expo == 0) { flags |= Underflow; } else if (expo >= Binary::maxExponent) { - expo = Binary::maxExponent; // Inf - flags |= Overflow; - if constexpr (Binary::bits == 80) { // x87 - fraction = IntType{1} << 63; - } else { - fraction = 0; + if (RoundOverflowToHuge(rounding, isNegative)) { + expo = Binary::maxExponent - 1; + fraction = mask; + } else { // Inf + expo = Binary::maxExponent; + flags |= Overflow; + if constexpr (Binary::bits == 80) { // x87 + fraction = IntType{1} << 63; + } else { + fraction = 0; + } } } using Raw = typename Binary::RawType; @@ -355,8 +369,12 @@ BigRadixFloatingPointNumber::ConvertToBinary() { } else { // underflow to +/-0. return {Real{SignBit()}, flags}; } - } else if (exponent_ > crazy) { // overflow to +/-Inf. - return {Real{Infinity()}, Overflow}; + } else if (exponent_ > crazy) { // overflow to +/-HUGE() or +/-Inf + if (RoundOverflowToHuge(rounding_, isNegative_)) { + return {Real{HUGE()}}; + } else { + return {Real{Infinity()}, Overflow}; + } } // Apply any negative decimal exponent by multiplication // by a power of two, adjusting the binary exponent to compensate. diff --git a/flang/runtime/edit-input.cpp b/flang/runtime/edit-input.cpp index 6e26e523c5ca7..2b80974906777 100644 --- a/flang/runtime/edit-input.cpp +++ b/flang/runtime/edit-input.cpp @@ -650,15 +650,23 @@ decimal::ConversionToBinaryResult ConvertHexadecimal( expo = 0; // subnormal flags |= decimal::Underflow; } else if (expo >= RealType::maxExponent) { - expo = RealType::maxExponent; // +/-Inf - fraction = 0; + if (rounding == decimal::RoundToZero || + (rounding == decimal::RoundDown && !isNegative) || + (rounding == decimal::RoundUp && isNegative)) { + expo = RealType::maxExponent - 1; // +/-HUGE() + fraction = significandMask; + } else { + expo = RealType::maxExponent; // +/-Inf + fraction = 0; + flags |= decimal::Overflow; + } } else { fraction &= significandMask; // remove explicit normalization unless x87 } return decimal::ConversionToBinaryResult{ RealType{static_cast(signBit | static_cast(expo) << RealType::significandBits | fraction)}, - static_cast(flags)}; + static_cast(flags)}; } template diff --git a/flang/unittests/Runtime/NumericalFormatTest.cpp b/flang/unittests/Runtime/NumericalFormatTest.cpp index 69637d8c6cb4c..bf954a84444ac 100644 --- a/flang/unittests/Runtime/NumericalFormatTest.cpp +++ b/flang/unittests/Runtime/NumericalFormatTest.cpp @@ -856,49 +856,66 @@ TEST(IOApiTests, FormatIntegerValues) { // Ensure double input values correctly map to raw uint64 values TEST(IOApiTests, EditDoubleInputValues) { - using TestCaseTy = std::tuple; + using TestCaseTy = std::tuple; + int ovf{IostatRealInputOverflow}; static const std::vector testCases{ - {"(F18.0)", " 0", 0x0}, - {"(F18.0)", " ", 0x0}, - {"(F18.0)", " -0", 0x8000000000000000}, - {"(F18.0)", " 01", 0x3ff0000000000000}, - {"(F18.0)", " 1", 0x3ff0000000000000}, - {"(F18.0)", " 125.", 0x405f400000000000}, - {"(F18.0)", " 12.5", 0x4029000000000000}, - {"(F18.0)", " 1.25", 0x3ff4000000000000}, - {"(F18.0)", " 01.25", 0x3ff4000000000000}, - {"(F18.0)", " .125", 0x3fc0000000000000}, - {"(F18.0)", " 0.125", 0x3fc0000000000000}, - {"(F18.0)", " .0625", 0x3fb0000000000000}, - {"(F18.0)", " 0.0625", 0x3fb0000000000000}, - {"(F18.0)", " 125", 0x405f400000000000}, - {"(F18.1)", " 125", 0x4029000000000000}, - {"(F18.2)", " 125", 0x3ff4000000000000}, - {"(F18.3)", " 125", 0x3fc0000000000000}, - {"(-1P,F18.0)", " 125", 0x4093880000000000}, // 1250 - {"(1P,F18.0)", " 125", 0x4029000000000000}, // 12.5 - {"(BZ,F18.0)", " 125 ", 0x4093880000000000}, // 1250 - {"(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000}, // 1.25e13 - {"(BZ,F18.0)", " . ", 0x0}, - {"(BZ,F18.0)", " . e +1 ", 0x0}, - {"(DC,F18.0)", " 12,5", 0x4029000000000000}, - {"(EX22.0)", "0X0P0 ", 0x0}, // +0. - {"(EX22.0)", "-0X0P0 ", 0x8000000000000000}, // -0. - {"(EX22.0)", "0X.8P1 ", 0x3ff0000000000000}, // 1.0 - {"(EX22.0)", "0X8.P-3 ", 0x3ff0000000000000}, // 1.0 - {"(EX22.0)", "0X.1P4 ", 0x3ff0000000000000}, // 1.0 - {"(EX22.0)", "0X10.P-4 ", 0x3ff0000000000000}, // 1.0 - {"(EX22.0)", "0X8.00P-3 ", 0x3ff0000000000000}, // 1.0 - {"(EX22.0)", "0X80.0P-6 ", 0x4000000000000000}, // 2.0 - {"(EX22.0)", "0XC.CCCCCCCCCCCDP-7 ", 0x3fb999999999999a}, // 0.1 - {"(EX22.0)", "0X.8P-1021 ", 0x0010000000000000}, // min normal - {"(EX22.0)", "0X.8P-1022 ", 0x0008000000000000}, // subnormal - {"(EX22.0)", "0X.8P-1073 ", 0x0000000000000001}, // min subn. - {"(EX22.0)", "0X.FFFFFFFFFFFFF8P1024", 0x7fefffffffffffff}, // max finite - {"(EX22.0)", "0X.8P1025 ", 0x7ff0000000000000}, // +Inf - {"(EX22.0)", "-0X.8P1025 ", 0xfff0000000000000}, // -Inf + {"(F18.0)", " 0", 0x0, 0}, + {"(F18.0)", " ", 0x0, 0}, + {"(F18.0)", " -0", 0x8000000000000000, 0}, + {"(F18.0)", " 01", 0x3ff0000000000000, 0}, + {"(F18.0)", " 1", 0x3ff0000000000000, 0}, + {"(F18.0)", " 125.", 0x405f400000000000, 0}, + {"(F18.0)", " 12.5", 0x4029000000000000, 0}, + {"(F18.0)", " 1.25", 0x3ff4000000000000, 0}, + {"(F18.0)", " 01.25", 0x3ff4000000000000, 0}, + {"(F18.0)", " .125", 0x3fc0000000000000, 0}, + {"(F18.0)", " 0.125", 0x3fc0000000000000, 0}, + {"(F18.0)", " .0625", 0x3fb0000000000000, 0}, + {"(F18.0)", " 0.0625", 0x3fb0000000000000, 0}, + {"(F18.0)", " 125", 0x405f400000000000, 0}, + {"(F18.1)", " 125", 0x4029000000000000, 0}, + {"(F18.2)", " 125", 0x3ff4000000000000, 0}, + {"(F18.3)", " 125", 0x3fc0000000000000, 0}, + {"(-1P,F18.0)", " 125", 0x4093880000000000, 0}, // 1250 + {"(1P,F18.0)", " 125", 0x4029000000000000, 0}, // 12.5 + {"(BZ,F18.0)", " 125 ", 0x4093880000000000, 0}, // 1250 + {"(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000, 0}, // 1.25e13 + {"(BZ,F18.0)", " . ", 0x0, 0}, + {"(BZ,F18.0)", " . e +1 ", 0x0, 0}, + {"(DC,F18.0)", " 12,5", 0x4029000000000000, 0}, + {"(EX22.0)", "0X0P0 ", 0x0, 0}, // +0. + {"(EX22.0)", "-0X0P0 ", 0x8000000000000000, 0}, // -0. + {"(EX22.0)", "0X.8P1 ", 0x3ff0000000000000, 0}, // 1.0 + {"(EX22.0)", "0X8.P-3 ", 0x3ff0000000000000, 0}, // 1.0 + {"(EX22.0)", "0X.1P4 ", 0x3ff0000000000000, 0}, // 1.0 + {"(EX22.0)", "0X10.P-4 ", 0x3ff0000000000000, 0}, // 1.0 + {"(EX22.0)", "0X8.00P-3 ", 0x3ff0000000000000, 0}, // 1.0 + {"(EX22.0)", "0X80.0P-6 ", 0x4000000000000000, 0}, // 2.0 + {"(EX22.0)", "0XC.CCCCCCCCCCCDP-7 ", 0x3fb999999999999a, 0}, // 0.1 + {"(EX22.0)", "0X.8P-1021 ", 0x0010000000000000, + 0}, // min normal + {"(EX22.0)", "0X.8P-1022 ", 0x0008000000000000, + 0}, // subnormal + {"(EX22.0)", "0X.8P-1073 ", 0x0000000000000001, + 0}, // min subn. + {"(EX22.0)", "0X.FFFFFFFFFFFFF8P1024", 0x7fefffffffffffff, + 0}, // max finite + {"(EX22.0)", "0X.8P1025 ", 0x7ff0000000000000, ovf}, // +Inf + {"(EX22.0)", "-0X.8P1025 ", 0xfff0000000000000, ovf}, // -Inf + {"(RZ,F7.0)", " 2.e308", 0x7fefffffffffffff, 0}, // +HUGE() + {"(RD,F7.0)", " 2.e308", 0x7fefffffffffffff, 0}, // +HUGE() + {"(RU,F7.0)", " 2.e308", 0x7ff0000000000000, ovf}, // +Inf + {"(RZ,F7.0)", "-2.e308", 0xffefffffffffffff, 0}, // -HUGE() + {"(RD,F7.0)", "-2.e308", 0xfff0000000000000, ovf}, // -Inf + {"(RU,F7.0)", "-2.e308", 0xffefffffffffffff, 0}, // -HUGE() + {"(RZ,F7.0)", " 1.e999", 0x7fefffffffffffff, 0}, // +HUGE() + {"(RD,F7.0)", " 1.e999", 0x7fefffffffffffff, 0}, // +HUGE() + {"(RU,F7.0)", " 1.e999", 0x7ff0000000000000, ovf}, // +Inf + {"(RZ,F7.0)", "-1.e999", 0xffefffffffffffff, 0}, // -HUGE() + {"(RD,F7.0)", "-1.e999", 0xfff0000000000000, ovf}, // -Inf + {"(RU,F7.0)", "-1.e999", 0xffefffffffffffff, 0}, // -HUGE() }; - for (auto const &[format, data, want] : testCases) { + for (auto const &[format, data, want, iostat] : testCases) { auto cookie{IONAME(BeginInternalFormattedInput)( data, std::strlen(data), format, std::strlen(format))}; union { @@ -915,12 +932,14 @@ TEST(IOApiTests, EditDoubleInputValues) { char iomsg[bufferSize]; std::memset(iomsg, '\0', bufferSize - 1); - // Ensure no errors were encountered reading input buffer into union value + // Ensure no unexpected errors were encountered reading input buffer into + // union value IONAME(GetIoMsg)(cookie, iomsg, bufferSize - 1); auto status{IONAME(EndIoStatement)(cookie)}; - ASSERT_EQ(status, 0) << '\'' << format << "' failed reading '" << data - << "', status " << static_cast(status) - << " iomsg '" << iomsg << "'"; + ASSERT_EQ(status, iostat) + << '\'' << format << "' failed reading '" << data << "', status " + << static_cast(status) << " != expected " << iostat << " iomsg '" + << iomsg << "'"; // Ensure raw uint64 value matches expected conversion from double ASSERT_EQ(u.raw, want) << '\'' << format << "' failed reading '" << data