Skip to content

Commit 50d15e6

Browse files
authored
[flang] Subnormal arguments to and results from SPACING (llvm#108861)
The standards aren't clear about how IEEE-754 subnormal values interact with the intrinsic function SPACING. Four compilers interpret the standard such that SPACING(x) will return a value never less than TINY(x); one compiler returns TINY(x) for ABS(x) <= TINY(x) but can return SPACING(x) < TINY(x) for some ABS(x) > TINY(x); one other compiler works similarly, but also oddly returns SPACING(x) < TINY(x) for ABS(x) >= TINY(x)/2. Follow the most common precedent.
1 parent eebe9a3 commit 50d15e6

File tree

3 files changed

+21
-16
lines changed

3 files changed

+21
-16
lines changed

flang/lib/Evaluate/real.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,11 +770,13 @@ template <typename W, int P> Real<W, P> Real<W, P>::SPACING() const {
770770
} else if (IsInfinite()) {
771771
return NotANumber();
772772
} else if (IsZero() || IsSubnormal()) {
773-
return TINY(); // mandated by standard
773+
return TINY(); // standard & 100% portable
774774
} else {
775775
Real result;
776776
result.Normalize(false, Exponent(), Fraction::MASKR(1));
777-
return result.IsZero() ? TINY() : result;
777+
// Can the result be less than TINY()? No, with five commonly
778+
// used compilers; yes, with two less commonly used ones.
779+
return result.IsZero() || result.IsSubnormal() ? TINY() : result;
778780
}
779781
}
780782

flang/runtime/numeric-templates.h

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -88,12 +88,16 @@ struct MaxOrMinIdentity<TypeCategory::Real, 16, IS_MAXVAL,
8888

8989
// Minimum finite representable value.
9090
// For floating-point types, returns minimum positive normalized value.
91-
template <typename T> struct MinValue {
91+
template <int PREC, typename T> struct MinValue {
9292
static RT_API_ATTRS T get() { return std::numeric_limits<T>::min(); }
9393
};
94+
template <typename T> struct MinValue<11, T> {
95+
// TINY(0._2)
96+
static constexpr RT_API_ATTRS T get() { return 0.00006103515625E-04; }
97+
};
9498

9599
#if HAS_FLOAT128
96-
template <> struct MinValue<CppTypeFor<TypeCategory::Real, 16>> {
100+
template <> struct MinValue<113, CppTypeFor<TypeCategory::Real, 16>> {
97101
using Type = CppTypeFor<TypeCategory::Real, 16>;
98102
static RT_API_ATTRS Type get() {
99103
// Create a buffer to store binary representation of __float128 constant.
@@ -167,8 +171,8 @@ template <> struct MAXTy<CppTypeFor<TypeCategory::Real, 16>> {
167171
};
168172
#endif
169173

170-
template <typename T> struct MINTy {
171-
static constexpr RT_API_ATTRS T compute() { return MinValue<T>::get(); }
174+
template <int PREC, typename T> struct MINTy {
175+
static constexpr RT_API_ATTRS T compute() { return MinValue<PREC, T>::get(); }
172176
};
173177

174178
template <typename T> struct QNANTy {
@@ -339,23 +343,20 @@ template <int PREC, typename T> inline RT_API_ATTRS T RRSpacing(T x) {
339343

340344
// SPACING (16.9.180)
341345
template <int PREC, typename T> inline RT_API_ATTRS T Spacing(T x) {
346+
T tiny{MINTy<PREC, T>::compute()};
342347
if (ISNANTy<T>::compute(x)) {
343348
return x; // NaN -> same NaN
344349
} else if (ISINFTy<T>::compute(x)) {
345350
return QNANTy<T>::compute(); // +/-Inf -> NaN
346351
} else if (x == 0) { // 0 -> TINY(x)
347-
// The standard-mandated behavior seems broken, since TINY() can't be
348-
// subnormal.
349-
if constexpr (PREC == 11) { // REAL(2)
350-
return 0.00006103515625E-04; // TINY(0._2)
351-
} else {
352-
// N.B. TINY(0._3) == TINY(0._4) so this works even if no std::bfloat16_t.
353-
return MINTy<T>::compute();
354-
}
352+
return tiny;
355353
} else {
356354
T result{LDEXPTy<T>::compute(
357355
static_cast<T>(1.0), ILOGBTy<T>::compute(x) + 1 - PREC)}; // 2**(e-p)
358-
return result == 0 ? /*TINY(x)*/ MINTy<T>::compute() : result;
356+
// All compilers return TINY(x) for |x| <= TINY(x), but differ over whether
357+
// SPACING(x) can be < TINY(x) for |x| > TINY(x). The most common precedent
358+
// is to never return a value < TINY(x).
359+
return result <= tiny ? tiny : result;
359360
}
360361
}
361362

flang/test/Evaluate/fold-spacing.f90

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ module m
55
logical, parameter :: test_2 = spacing(-3.0) == scale(1.0, -22)
66
logical, parameter :: test_3 = spacing(3.0d0) == scale(1.0, -51)
77
logical, parameter :: test_4 = spacing(0.) == tiny(0.)
8-
logical, parameter :: test_5 = spacing(tiny(0.)) == 1.e-45
8+
logical, parameter :: test_5a = spacing(tiny(0.)) == tiny(0.)
9+
logical, parameter :: test_5b = spacing(tiny(0.)/2) == tiny(0.)
10+
logical, parameter :: test_5c = spacing(tiny(0.)*2) == tiny(0.)
911
logical, parameter :: test_6 = spacing(8388608.) == 1.
1012
logical, parameter :: test_7 = spacing(spacing(tiny(.0))) == tiny(0.)
1113
logical, parameter :: test_11 = rrspacing(3.0) == scale(0.75, 24)

0 commit comments

Comments
 (0)