Skip to content

Commit 773f444

Browse files
committed
[flang][runtime] Detect & signal underflow when reading reals
Extend decimal->binary conversion to detect underflow cases and raise the corresponding floating-point exception. Underflow includes subnormal results.
1 parent ea3a3b2 commit 773f444

File tree

3 files changed

+28
-11
lines changed

3 files changed

+28
-11
lines changed

flang/include/flang/Decimal/decimal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ enum ConversionResultFlags {
3434
Overflow = 1,
3535
Inexact = 2,
3636
Invalid = 4,
37+
Underflow = 8,
3738
};
3839

3940
struct ConversionToDecimalResult {

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

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -256,12 +256,17 @@ ConversionToBinaryResult<PREC> IntermediateFloat<PREC>::ToBinary(
256256
if (guard != 0) {
257257
flags |= Inexact;
258258
}
259-
if (fraction == 0 && guard <= oneHalf) {
260-
if ((!isNegative && rounding == RoundUp) ||
261-
(isNegative && rounding == RoundDown)) {
262-
// round to minimum nonzero value
263-
} else {
264-
return {Binary{}, static_cast<enum ConversionResultFlags>(flags)};
259+
if (fraction == 0) {
260+
if (guard <= oneHalf) {
261+
if ((!isNegative && rounding == RoundUp) ||
262+
(isNegative && rounding == RoundDown)) {
263+
// round to minimum nonzero value
264+
} else { // round to zero
265+
if (guard != 0) {
266+
flags |= Underflow;
267+
}
268+
return {Binary{}, static_cast<enum ConversionResultFlags>(flags)};
269+
}
265270
}
266271
} else {
267272
// The value is nonzero; normalize it.
@@ -301,8 +306,10 @@ ConversionToBinaryResult<PREC> IntermediateFloat<PREC>::ToBinary(
301306
}
302307
if (expo == 1 && fraction < topBit) {
303308
expo = 0; // subnormal
304-
}
305-
if (expo >= Binary::maxExponent) {
309+
flags |= Underflow;
310+
} else if (expo == 0) {
311+
flags |= Underflow;
312+
} else if (expo >= Binary::maxExponent) {
306313
expo = Binary::maxExponent; // Inf
307314
flags |= Overflow;
308315
if constexpr (Binary::bits == 80) { // x87
@@ -338,11 +345,15 @@ BigRadixFloatingPointNumber<PREC, LOG10RADIX>::ConvertToBinary() {
338345
// Sanity checks for ridiculous exponents
339346
static constexpr int crazy{2 * Real::decimalRange + log10Radix};
340347
if (exponent_ < -crazy) {
348+
enum ConversionResultFlags flags {
349+
static_cast<enum ConversionResultFlags>(Inexact | Underflow)
350+
};
341351
if ((!isNegative_ && rounding_ == RoundUp) ||
342352
(isNegative_ && rounding_ == RoundDown)) {
343-
return {Real{Raw{1} | SignBit()}}; // return least nonzero value
353+
// return least nonzero value
354+
return {Real{Raw{1} | SignBit()}, flags};
344355
} else { // underflow to +/-0.
345-
return {Real{SignBit()}, Inexact};
356+
return {Real{SignBit()}, flags};
346357
}
347358
} else if (exponent_ > crazy) { // overflow to +/-Inf.
348359
return {Real{Infinity()}, Overflow};

flang/runtime/edit-input.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,9 @@ static void RaiseFPExceptions(decimal::ConversionResultFlags flags) {
478478
if (flags & decimal::ConversionResultFlags::Overflow) {
479479
RAISE(FE_OVERFLOW);
480480
}
481+
if (flags & decimal::ConversionResultFlags::Underflow) {
482+
RAISE(FE_UNDERFLOW);
483+
}
481484
if (flags & decimal::ConversionResultFlags::Inexact) {
482485
RAISE(FE_INEXACT);
483486
}
@@ -640,10 +643,12 @@ decimal::ConversionToBinaryResult<binaryPrecision> ConvertHexadecimal(
640643
}
641644
// Package & return result
642645
constexpr RawType significandMask{(one << RealType::significandBits) - 1};
646+
int flags{(roundingBit | guardBit) ? decimal::Inexact : decimal::Exact};
643647
if (!fraction) {
644648
expo = 0;
645649
} else if (expo == 1 && !(fraction >> (binaryPrecision - 1))) {
646650
expo = 0; // subnormal
651+
flags |= decimal::Underflow;
647652
} else if (expo >= RealType::maxExponent) {
648653
expo = RealType::maxExponent; // +/-Inf
649654
fraction = 0;
@@ -653,7 +658,7 @@ decimal::ConversionToBinaryResult<binaryPrecision> ConvertHexadecimal(
653658
return decimal::ConversionToBinaryResult<binaryPrecision>{
654659
RealType{static_cast<RawType>(signBit |
655660
static_cast<RawType>(expo) << RealType::significandBits | fraction)},
656-
(roundingBit | guardBit) ? decimal::Inexact : decimal::Exact};
661+
static_cast<enum decimal::ConversionResultFlags>(flags)};
657662
}
658663

659664
template <int KIND>

0 commit comments

Comments
 (0)