Skip to content

Commit 05c384e

Browse files
committed
[flang][runtime] Support SPACING for REAL(2 & 3)
Add runtime APIs for the intrinsic function SPACING for REAL kinds 2 & 3 in two ways: Spacing2 (& 3) for build environments with std::float16_t, and Spacing2By4 (& 3By4) variants (for any build environment) which compute SPACING for those types but accept and return their values as 32-bit floats. SPACING for REAL(2) is needed by HDF5.
1 parent 7cc789b commit 05c384e

File tree

5 files changed

+79
-6
lines changed

5 files changed

+79
-6
lines changed

flang/include/flang/Runtime/cpp-type.h

+32-4
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,20 @@
1414
#include "flang/Common/Fortran.h"
1515
#include "flang/Common/float128.h"
1616
#include "flang/Common/uint128.h"
17-
#include <cfloat>
1817
#include <complex>
1918
#include <cstdint>
19+
#if __cplusplus >= 202302
20+
#include <stdfloat>
21+
#endif
2022
#include <type_traits>
2123

24+
#if !defined HAS_FP16 && __STDCPP_FLOAT16_T__
25+
#define HAS_FP16 1
26+
#endif
27+
#if !defined HAS_BF16 && __STDCPP_BFLOAT16_T__
28+
#define HAS_BF16 1
29+
#endif
30+
2231
namespace Fortran::runtime {
2332

2433
using common::TypeCategory;
@@ -37,24 +46,43 @@ template <int KIND> struct CppTypeForHelper<TypeCategory::Integer, KIND> {
3746
using type = common::HostSignedIntType<8 * KIND>;
3847
};
3948

40-
// TODO: REAL/COMPLEX(2 & 3)
49+
#if HAS_FP16
50+
template <> struct CppTypeForHelper<TypeCategory::Real, 2> {
51+
using type = std::float16_t;
52+
};
53+
#endif
54+
#if HAS_BF16
55+
template <> struct CppTypeForHelper<TypeCategory::Real, 3> {
56+
using type = std::bfloat16_t;
57+
};
58+
#endif
4159
template <> struct CppTypeForHelper<TypeCategory::Real, 4> {
60+
#if __STDCPP_FLOAT32_T__
61+
using type = std::float32_t;
62+
#else
4263
using type = float;
64+
#endif
4365
};
4466
template <> struct CppTypeForHelper<TypeCategory::Real, 8> {
67+
#if __STDCPP_FLOAT64_T__
68+
using type = std::float64_t;
69+
#else
4570
using type = double;
71+
#endif
4672
};
4773
#if LDBL_MANT_DIG == 64
4874
template <> struct CppTypeForHelper<TypeCategory::Real, 10> {
4975
using type = long double;
5076
};
5177
#endif
52-
#if LDBL_MANT_DIG == 113
78+
#if __STDCPP_FLOAT128_T__
79+
using CppFloat128Type = std::float128_t;
80+
#elif LDBL_MANT_DIG == 113
5381
using CppFloat128Type = long double;
5482
#elif HAS_FLOAT128
5583
using CppFloat128Type = __float128;
5684
#endif
57-
#if LDBL_MANT_DIG == 113 || HAS_FLOAT128
85+
#if __STDCPP_FLOAT128_t || LDBL_MANT_DIG == 113 || HAS_FLOAT128
5886
template <> struct CppTypeForHelper<TypeCategory::Real, 16> {
5987
using type = CppFloat128Type;
6088
};

flang/include/flang/Runtime/numeric.h

+15
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,21 @@ CppTypeFor<TypeCategory::Integer, 4> RTDECL(SelectedRealKindMasked)(
391391
const char *, int, void *, int, void *, int, void *, int, int);
392392

393393
// SPACING
394+
// The variants Spacing2By4 and Spacing3By4 compute SPACING for REAL(2/3)
395+
// but accept and return REAL(4) values, for use in environments where
396+
// std::float16_t or std::bfloat16_t are unavailable.
397+
#if HAS_FP16
398+
CppTypeFor<TypeCategory::Real, 2> RTDECL(Spacing2)(
399+
CppTypeFor<TypeCategory::Real, 2>);
400+
#endif
401+
CppTypeFor<TypeCategory::Real, 4> RTDECL(Spacing2By4)(
402+
CppTypeFor<TypeCategory::Real, 4>);
403+
#if HAS_BF16
404+
CppTypeFor<TypeCategory::Real, 3> RTDECL(Spacing3)(
405+
CppTypeFor<TypeCategory::Real, 3>);
406+
#endif
407+
CppTypeFor<TypeCategory::Real, 4> RTDECL(Spacing3By4)(
408+
CppTypeFor<TypeCategory::Real, 4>);
394409
CppTypeFor<TypeCategory::Real, 4> RTDECL(Spacing4)(
395410
CppTypeFor<TypeCategory::Real, 4>);
396411
CppTypeFor<TypeCategory::Real, 8> RTDECL(Spacing8)(

flang/runtime/numeric-templates.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -343,10 +343,15 @@ template <int PREC, typename T> inline RT_API_ATTRS T Spacing(T x) {
343343
return x; // NaN -> same NaN
344344
} else if (ISINFTy<T>::compute(x)) {
345345
return QNANTy<T>::compute(); // +/-Inf -> NaN
346-
} else if (x == 0) {
346+
} else if (x == 0) { // 0 -> TINY(x)
347347
// The standard-mandated behavior seems broken, since TINY() can't be
348348
// subnormal.
349-
return MINTy<T>::compute(); // 0 -> TINY(x)
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+
}
350355
} else {
351356
T result{LDEXPTy<T>::compute(
352357
static_cast<T>(1.0), ILOGBTy<T>::compute(x) + 1 - PREC)}; // 2**(e-p)

flang/runtime/numeric.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,26 @@ CppTypeFor<TypeCategory::Integer, 4> RTDEF(SelectedRealKindMasked)(
848848
return SelectedRealKind(p, r, d, mask);
849849
}
850850

851+
#if HAS_FP16
852+
CppTypeFor<TypeCategory::Real, 2> RTDEF(Spacing2)(
853+
CppTypeFor<TypeCategory::Real, 2> x) {
854+
return Spacing<11>(x);
855+
}
856+
#endif
857+
CppTypeFor<TypeCategory::Real, 4> RTDEF(Spacing2By4)(
858+
CppTypeFor<TypeCategory::Real, 4> x) {
859+
return Spacing<11>(x);
860+
}
861+
#if HAS_BF16
862+
CppTypeFor<TypeCategory::Real, 3> RTDEF(Spacing3)(
863+
CppTypeFor<TypeCategory::Real, 3> x) {
864+
return Spacing<8>(x);
865+
}
866+
#endif
867+
CppTypeFor<TypeCategory::Real, 4> RTDEF(Spacing3By4)(
868+
CppTypeFor<TypeCategory::Real, 4> x) {
869+
return Spacing<8>(x);
870+
}
851871
CppTypeFor<TypeCategory::Real, 4> RTDEF(Spacing4)(
852872
CppTypeFor<TypeCategory::Real, 4> x) {
853873
return Spacing<24>(x);

flang/unittests/Runtime/Numeric.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,11 @@ TEST(Numeric, Spacing) {
259259
std::isnan(RTNAME(Spacing4)(std::numeric_limits<Real<4>>::infinity())));
260260
EXPECT_TRUE(
261261
std::isnan(RTNAME(Spacing8)(std::numeric_limits<Real<8>>::quiet_NaN())));
262+
EXPECT_EQ(RTNAME(Spacing2By4)(Real<4>{3.0}), std::ldexp(Real<4>{1.0}, -9));
263+
EXPECT_EQ(RTNAME(Spacing2By4)(Real<4>{0.0}), Real<4>{0.00006103515625E-04});
264+
EXPECT_EQ(RTNAME(Spacing3By4)(Real<4>{3.0}), std::ldexp(Real<4>{1.0}, -6));
265+
EXPECT_EQ(
266+
RTNAME(Spacing3By4)(Real<4>{0.0}), std::numeric_limits<Real<4>>::min());
262267
}
263268

264269
TEST(Numeric, FPowI) {

0 commit comments

Comments
 (0)