Skip to content

Commit 1346037

Browse files
authored
[flang][runtime] Return +/-HUGE() for some real input roundings (#75525)
The Fortran standard says that overflow input cases in some rounding modes (RZ, RD, RU) should round to a "representable" number. Some Fortran compilers interpret this to mean +/-HUGE(), some as +/-Inf. Follow the precedent of gfortran and the Intel compilers.
1 parent 9469dc3 commit 1346037

File tree

4 files changed

+108
-57
lines changed

4 files changed

+108
-57
lines changed

flang/lib/Decimal/big-radix-floating-point.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,12 @@ template <int PREC, int LOG10RADIX = 16> class BigRadixFloatingPointNumber {
369369
}
370370
return result;
371371
}
372+
constexpr Raw HUGE() const {
373+
Raw result{static_cast<Raw>(Real::maxExponent)};
374+
result <<= Real::significandBits;
375+
result |= SignBit();
376+
return result - 1; // decrement exponent, set all significand bits
377+
}
372378

373379
Digit digit_[maxDigits]; // in little-endian order: digit_[0] is LSD
374380
int digits_{0}; // # of elements in digit_[] array; zero when zero

flang/lib/Decimal/decimal-to-binary.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,15 @@ template <int PREC> class IntermediateFloat {
237237
int exponent_{0};
238238
};
239239

240+
// The standard says that these overflow cases round to "representable"
241+
// numbers, and some popular compilers interpret that to mean +/-HUGE()
242+
// rather than +/-Inf.
243+
static inline constexpr bool RoundOverflowToHuge(
244+
enum FortranRounding rounding, bool isNegative) {
245+
return rounding == RoundToZero || (!isNegative && rounding == RoundDown) ||
246+
(isNegative && rounding == RoundUp);
247+
}
248+
240249
template <int PREC>
241250
ConversionToBinaryResult<PREC> IntermediateFloat<PREC>::ToBinary(
242251
bool isNegative, FortranRounding rounding) const {
@@ -260,7 +269,7 @@ ConversionToBinaryResult<PREC> IntermediateFloat<PREC>::ToBinary(
260269
if (guard <= oneHalf) {
261270
if ((!isNegative && rounding == RoundUp) ||
262271
(isNegative && rounding == RoundDown)) {
263-
// round to minimum nonzero value
272+
// round to least nonzero value
264273
} else { // round to zero
265274
if (guard != 0) {
266275
flags |= Underflow;
@@ -310,12 +319,17 @@ ConversionToBinaryResult<PREC> IntermediateFloat<PREC>::ToBinary(
310319
} else if (expo == 0) {
311320
flags |= Underflow;
312321
} else if (expo >= Binary::maxExponent) {
313-
expo = Binary::maxExponent; // Inf
314-
flags |= Overflow;
315-
if constexpr (Binary::bits == 80) { // x87
316-
fraction = IntType{1} << 63;
317-
} else {
318-
fraction = 0;
322+
if (RoundOverflowToHuge(rounding, isNegative)) {
323+
expo = Binary::maxExponent - 1;
324+
fraction = mask;
325+
} else { // Inf
326+
expo = Binary::maxExponent;
327+
flags |= Overflow;
328+
if constexpr (Binary::bits == 80) { // x87
329+
fraction = IntType{1} << 63;
330+
} else {
331+
fraction = 0;
332+
}
319333
}
320334
}
321335
using Raw = typename Binary::RawType;
@@ -355,8 +369,12 @@ BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToBinary() {
355369
} else { // underflow to +/-0.
356370
return {Real{SignBit()}, flags};
357371
}
358-
} else if (exponent_ > crazy) { // overflow to +/-Inf.
359-
return {Real{Infinity()}, Overflow};
372+
} else if (exponent_ > crazy) { // overflow to +/-HUGE() or +/-Inf
373+
if (RoundOverflowToHuge(rounding_, isNegative_)) {
374+
return {Real{HUGE()}};
375+
} else {
376+
return {Real{Infinity()}, Overflow};
377+
}
360378
}
361379
// Apply any negative decimal exponent by multiplication
362380
// by a power of two, adjusting the binary exponent to compensate.

flang/runtime/edit-input.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -650,15 +650,23 @@ decimal::ConversionToBinaryResult<binaryPrecision> ConvertHexadecimal(
650650
expo = 0; // subnormal
651651
flags |= decimal::Underflow;
652652
} else if (expo >= RealType::maxExponent) {
653-
expo = RealType::maxExponent; // +/-Inf
654-
fraction = 0;
653+
if (rounding == decimal::RoundToZero ||
654+
(rounding == decimal::RoundDown && !isNegative) ||
655+
(rounding == decimal::RoundUp && isNegative)) {
656+
expo = RealType::maxExponent - 1; // +/-HUGE()
657+
fraction = significandMask;
658+
} else {
659+
expo = RealType::maxExponent; // +/-Inf
660+
fraction = 0;
661+
flags |= decimal::Overflow;
662+
}
655663
} else {
656664
fraction &= significandMask; // remove explicit normalization unless x87
657665
}
658666
return decimal::ConversionToBinaryResult<binaryPrecision>{
659667
RealType{static_cast<RawType>(signBit |
660668
static_cast<RawType>(expo) << RealType::significandBits | fraction)},
661-
static_cast<enum decimal::ConversionResultFlags>(flags)};
669+
static_cast<decimal::ConversionResultFlags>(flags)};
662670
}
663671

664672
template <int KIND>

flang/unittests/Runtime/NumericalFormatTest.cpp

Lines changed: 64 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -856,49 +856,66 @@ TEST(IOApiTests, FormatIntegerValues) {
856856

857857
// Ensure double input values correctly map to raw uint64 values
858858
TEST(IOApiTests, EditDoubleInputValues) {
859-
using TestCaseTy = std::tuple<const char *, const char *, std::uint64_t>;
859+
using TestCaseTy = std::tuple<const char *, const char *, std::uint64_t, int>;
860+
int ovf{IostatRealInputOverflow};
860861
static const std::vector<TestCaseTy> testCases{
861-
{"(F18.0)", " 0", 0x0},
862-
{"(F18.0)", " ", 0x0},
863-
{"(F18.0)", " -0", 0x8000000000000000},
864-
{"(F18.0)", " 01", 0x3ff0000000000000},
865-
{"(F18.0)", " 1", 0x3ff0000000000000},
866-
{"(F18.0)", " 125.", 0x405f400000000000},
867-
{"(F18.0)", " 12.5", 0x4029000000000000},
868-
{"(F18.0)", " 1.25", 0x3ff4000000000000},
869-
{"(F18.0)", " 01.25", 0x3ff4000000000000},
870-
{"(F18.0)", " .125", 0x3fc0000000000000},
871-
{"(F18.0)", " 0.125", 0x3fc0000000000000},
872-
{"(F18.0)", " .0625", 0x3fb0000000000000},
873-
{"(F18.0)", " 0.0625", 0x3fb0000000000000},
874-
{"(F18.0)", " 125", 0x405f400000000000},
875-
{"(F18.1)", " 125", 0x4029000000000000},
876-
{"(F18.2)", " 125", 0x3ff4000000000000},
877-
{"(F18.3)", " 125", 0x3fc0000000000000},
878-
{"(-1P,F18.0)", " 125", 0x4093880000000000}, // 1250
879-
{"(1P,F18.0)", " 125", 0x4029000000000000}, // 12.5
880-
{"(BZ,F18.0)", " 125 ", 0x4093880000000000}, // 1250
881-
{"(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000}, // 1.25e13
882-
{"(BZ,F18.0)", " . ", 0x0},
883-
{"(BZ,F18.0)", " . e +1 ", 0x0},
884-
{"(DC,F18.0)", " 12,5", 0x4029000000000000},
885-
{"(EX22.0)", "0X0P0 ", 0x0}, // +0.
886-
{"(EX22.0)", "-0X0P0 ", 0x8000000000000000}, // -0.
887-
{"(EX22.0)", "0X.8P1 ", 0x3ff0000000000000}, // 1.0
888-
{"(EX22.0)", "0X8.P-3 ", 0x3ff0000000000000}, // 1.0
889-
{"(EX22.0)", "0X.1P4 ", 0x3ff0000000000000}, // 1.0
890-
{"(EX22.0)", "0X10.P-4 ", 0x3ff0000000000000}, // 1.0
891-
{"(EX22.0)", "0X8.00P-3 ", 0x3ff0000000000000}, // 1.0
892-
{"(EX22.0)", "0X80.0P-6 ", 0x4000000000000000}, // 2.0
893-
{"(EX22.0)", "0XC.CCCCCCCCCCCDP-7 ", 0x3fb999999999999a}, // 0.1
894-
{"(EX22.0)", "0X.8P-1021 ", 0x0010000000000000}, // min normal
895-
{"(EX22.0)", "0X.8P-1022 ", 0x0008000000000000}, // subnormal
896-
{"(EX22.0)", "0X.8P-1073 ", 0x0000000000000001}, // min subn.
897-
{"(EX22.0)", "0X.FFFFFFFFFFFFF8P1024", 0x7fefffffffffffff}, // max finite
898-
{"(EX22.0)", "0X.8P1025 ", 0x7ff0000000000000}, // +Inf
899-
{"(EX22.0)", "-0X.8P1025 ", 0xfff0000000000000}, // -Inf
862+
{"(F18.0)", " 0", 0x0, 0},
863+
{"(F18.0)", " ", 0x0, 0},
864+
{"(F18.0)", " -0", 0x8000000000000000, 0},
865+
{"(F18.0)", " 01", 0x3ff0000000000000, 0},
866+
{"(F18.0)", " 1", 0x3ff0000000000000, 0},
867+
{"(F18.0)", " 125.", 0x405f400000000000, 0},
868+
{"(F18.0)", " 12.5", 0x4029000000000000, 0},
869+
{"(F18.0)", " 1.25", 0x3ff4000000000000, 0},
870+
{"(F18.0)", " 01.25", 0x3ff4000000000000, 0},
871+
{"(F18.0)", " .125", 0x3fc0000000000000, 0},
872+
{"(F18.0)", " 0.125", 0x3fc0000000000000, 0},
873+
{"(F18.0)", " .0625", 0x3fb0000000000000, 0},
874+
{"(F18.0)", " 0.0625", 0x3fb0000000000000, 0},
875+
{"(F18.0)", " 125", 0x405f400000000000, 0},
876+
{"(F18.1)", " 125", 0x4029000000000000, 0},
877+
{"(F18.2)", " 125", 0x3ff4000000000000, 0},
878+
{"(F18.3)", " 125", 0x3fc0000000000000, 0},
879+
{"(-1P,F18.0)", " 125", 0x4093880000000000, 0}, // 1250
880+
{"(1P,F18.0)", " 125", 0x4029000000000000, 0}, // 12.5
881+
{"(BZ,F18.0)", " 125 ", 0x4093880000000000, 0}, // 1250
882+
{"(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000, 0}, // 1.25e13
883+
{"(BZ,F18.0)", " . ", 0x0, 0},
884+
{"(BZ,F18.0)", " . e +1 ", 0x0, 0},
885+
{"(DC,F18.0)", " 12,5", 0x4029000000000000, 0},
886+
{"(EX22.0)", "0X0P0 ", 0x0, 0}, // +0.
887+
{"(EX22.0)", "-0X0P0 ", 0x8000000000000000, 0}, // -0.
888+
{"(EX22.0)", "0X.8P1 ", 0x3ff0000000000000, 0}, // 1.0
889+
{"(EX22.0)", "0X8.P-3 ", 0x3ff0000000000000, 0}, // 1.0
890+
{"(EX22.0)", "0X.1P4 ", 0x3ff0000000000000, 0}, // 1.0
891+
{"(EX22.0)", "0X10.P-4 ", 0x3ff0000000000000, 0}, // 1.0
892+
{"(EX22.0)", "0X8.00P-3 ", 0x3ff0000000000000, 0}, // 1.0
893+
{"(EX22.0)", "0X80.0P-6 ", 0x4000000000000000, 0}, // 2.0
894+
{"(EX22.0)", "0XC.CCCCCCCCCCCDP-7 ", 0x3fb999999999999a, 0}, // 0.1
895+
{"(EX22.0)", "0X.8P-1021 ", 0x0010000000000000,
896+
0}, // min normal
897+
{"(EX22.0)", "0X.8P-1022 ", 0x0008000000000000,
898+
0}, // subnormal
899+
{"(EX22.0)", "0X.8P-1073 ", 0x0000000000000001,
900+
0}, // min subn.
901+
{"(EX22.0)", "0X.FFFFFFFFFFFFF8P1024", 0x7fefffffffffffff,
902+
0}, // max finite
903+
{"(EX22.0)", "0X.8P1025 ", 0x7ff0000000000000, ovf}, // +Inf
904+
{"(EX22.0)", "-0X.8P1025 ", 0xfff0000000000000, ovf}, // -Inf
905+
{"(RZ,F7.0)", " 2.e308", 0x7fefffffffffffff, 0}, // +HUGE()
906+
{"(RD,F7.0)", " 2.e308", 0x7fefffffffffffff, 0}, // +HUGE()
907+
{"(RU,F7.0)", " 2.e308", 0x7ff0000000000000, ovf}, // +Inf
908+
{"(RZ,F7.0)", "-2.e308", 0xffefffffffffffff, 0}, // -HUGE()
909+
{"(RD,F7.0)", "-2.e308", 0xfff0000000000000, ovf}, // -Inf
910+
{"(RU,F7.0)", "-2.e308", 0xffefffffffffffff, 0}, // -HUGE()
911+
{"(RZ,F7.0)", " 1.e999", 0x7fefffffffffffff, 0}, // +HUGE()
912+
{"(RD,F7.0)", " 1.e999", 0x7fefffffffffffff, 0}, // +HUGE()
913+
{"(RU,F7.0)", " 1.e999", 0x7ff0000000000000, ovf}, // +Inf
914+
{"(RZ,F7.0)", "-1.e999", 0xffefffffffffffff, 0}, // -HUGE()
915+
{"(RD,F7.0)", "-1.e999", 0xfff0000000000000, ovf}, // -Inf
916+
{"(RU,F7.0)", "-1.e999", 0xffefffffffffffff, 0}, // -HUGE()
900917
};
901-
for (auto const &[format, data, want] : testCases) {
918+
for (auto const &[format, data, want, iostat] : testCases) {
902919
auto cookie{IONAME(BeginInternalFormattedInput)(
903920
data, std::strlen(data), format, std::strlen(format))};
904921
union {
@@ -915,12 +932,14 @@ TEST(IOApiTests, EditDoubleInputValues) {
915932
char iomsg[bufferSize];
916933
std::memset(iomsg, '\0', bufferSize - 1);
917934

918-
// Ensure no errors were encountered reading input buffer into union value
935+
// Ensure no unexpected errors were encountered reading input buffer into
936+
// union value
919937
IONAME(GetIoMsg)(cookie, iomsg, bufferSize - 1);
920938
auto status{IONAME(EndIoStatement)(cookie)};
921-
ASSERT_EQ(status, 0) << '\'' << format << "' failed reading '" << data
922-
<< "', status " << static_cast<int>(status)
923-
<< " iomsg '" << iomsg << "'";
939+
ASSERT_EQ(status, iostat)
940+
<< '\'' << format << "' failed reading '" << data << "', status "
941+
<< static_cast<int>(status) << " != expected " << iostat << " iomsg '"
942+
<< iomsg << "'";
924943

925944
// Ensure raw uint64 value matches expected conversion from double
926945
ASSERT_EQ(u.raw, want) << '\'' << format << "' failed reading '" << data

0 commit comments

Comments
 (0)