FLANG
numeric-templates.h
1//===-- runtime/numeric-templates.h -----------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9// Generic class and function templates used for implementing
10// various numeric intrinsics (EXPONENT, FRACTION, etc.).
11//
12// This header file also defines generic templates for "basic"
13// math operations like abs, isnan, etc. The Float128Math
14// library provides specializations for these templates
15// for the data type corresponding to CppTypeFor<TypeCategory::Real, 16>
16// on the target.
17
18#ifndef FORTRAN_RUNTIME_NUMERIC_TEMPLATES_H_
19#define FORTRAN_RUNTIME_NUMERIC_TEMPLATES_H_
20
21#include "terminator.h"
22#include "tools.h"
23#include "flang/Common/api-attrs.h"
24#include "flang/Common/erfc-scaled.h"
25#include "flang/Common/float128.h"
26#include <cstdint>
27#include <limits>
28
29namespace Fortran::runtime {
30
31// MAX/MIN/LOWEST values for different data types.
32
33// MaxOrMinIdentity returns MAX or LOWEST value of the given type.
34template <TypeCategory CAT, int KIND, bool IS_MAXVAL, typename Enable = void>
36 using Type = CppTypeFor<CAT, KIND>;
37 static constexpr RT_API_ATTRS Type Value() {
38 return IS_MAXVAL ? std::numeric_limits<Type>::lowest()
39 : std::numeric_limits<Type>::max();
40 }
41};
42
43// std::numeric_limits<> may not know int128_t
44template <bool IS_MAXVAL>
45struct MaxOrMinIdentity<TypeCategory::Integer, 16, IS_MAXVAL> {
46 using Type = CppTypeFor<TypeCategory::Integer, 16>;
47 static constexpr RT_API_ATTRS Type Value() {
48 return IS_MAXVAL ? Type{1} << 127 : ~Type{0} >> 1;
49 }
50};
51
52#if HAS_FLOAT128
53// std::numeric_limits<> may not support __float128.
54//
55// Usage of GCC quadmath.h's FLT128_MAX is complicated by the fact that
56// even GCC complains about 'Q' literal suffix under -Wpedantic.
57// We just recreate FLT128_MAX ourselves.
58//
59// This specialization must engage only when
60// CppTypeFor<TypeCategory::Real, 16> is __float128.
61template <bool IS_MAXVAL>
62struct MaxOrMinIdentity<TypeCategory::Real, 16, IS_MAXVAL,
63 typename std::enable_if_t<
64 std::is_same_v<CppTypeFor<TypeCategory::Real, 16>, __float128>>> {
65 using Type = __float128;
66 static RT_API_ATTRS Type Value() {
67 // Create a buffer to store binary representation of __float128 constant.
68 constexpr std::size_t alignment =
69 std::max(alignof(Type), alignof(std::uint64_t));
70 alignas(alignment) char data[sizeof(Type)];
71
72 // First, verify that our interpretation of __float128 format is correct,
73 // e.g. by checking at least one known constant.
74 *reinterpret_cast<Type *>(data) = Type(1.0);
75 if (*reinterpret_cast<std::uint64_t *>(data) != 0 ||
76 *(reinterpret_cast<std::uint64_t *>(data) + 1) != 0x3FFF000000000000) {
77 Terminator terminator{__FILE__, __LINE__};
78 terminator.Crash("not yet implemented: no full support for __float128");
79 }
80
81 // Recreate FLT128_MAX.
82 *reinterpret_cast<std::uint64_t *>(data) = 0xFFFFFFFFFFFFFFFF;
83 *(reinterpret_cast<std::uint64_t *>(data) + 1) = 0x7FFEFFFFFFFFFFFF;
84 Type max = *reinterpret_cast<Type *>(data);
85 return IS_MAXVAL ? -max : max;
86 }
87};
88#endif // HAS_FLOAT128
89
90// Minimum finite representable value.
91// For floating-point types, returns minimum positive normalized value.
92template <int PREC, typename T> struct MinValue {
93 static RT_API_ATTRS T get() { return std::numeric_limits<T>::min(); }
94};
95template <typename T> struct MinValue<11, T> {
96 // TINY(0._2)
97 static constexpr RT_API_ATTRS T get() { return 0.00006103515625E-04; }
98};
99
100#if HAS_FLOAT128
101template <> struct MinValue<113, CppTypeFor<TypeCategory::Real, 16>> {
102 using Type = CppTypeFor<TypeCategory::Real, 16>;
103 static RT_API_ATTRS Type get() {
104 // Create a buffer to store binary representation of __float128 constant.
105 constexpr std::size_t alignment =
106 std::max(alignof(Type), alignof(std::uint64_t));
107 alignas(alignment) char data[sizeof(Type)];
108
109 // First, verify that our interpretation of __float128 format is correct,
110 // e.g. by checking at least one known constant.
111 *reinterpret_cast<Type *>(data) = Type(1.0);
112 if (*reinterpret_cast<std::uint64_t *>(data) != 0 ||
113 *(reinterpret_cast<std::uint64_t *>(data) + 1) != 0x3FFF000000000000) {
114 Terminator terminator{__FILE__, __LINE__};
115 terminator.Crash("not yet implemented: no full support for __float128");
116 }
117
118 // Recreate FLT128_MIN.
119 *reinterpret_cast<std::uint64_t *>(data) = 0;
120 *(reinterpret_cast<std::uint64_t *>(data) + 1) = 0x1000000000000;
121 return *reinterpret_cast<Type *>(data);
122 }
123};
124#endif // HAS_FLOAT128
125
126template <typename T> struct ABSTy {
127 static constexpr RT_API_ATTRS T compute(T x) { return std::abs(x); }
128};
129
130// Suppress the warnings about calling __host__-only
131// 'long double' std::frexp, from __device__ code.
132RT_DIAG_PUSH
133RT_DIAG_DISABLE_CALL_HOST_FROM_DEVICE_WARN
134
135template <typename T> struct FREXPTy {
136 static constexpr RT_API_ATTRS T compute(T x, int *e) {
137 return std::frexp(x, e);
138 }
139};
140
141RT_DIAG_POP
142
143template <typename T> struct ILOGBTy {
144 static constexpr RT_API_ATTRS int compute(T x) { return std::ilogb(x); }
145};
146
147template <typename T> struct ISINFTy {
148 static constexpr RT_API_ATTRS bool compute(T x) { return std::isinf(x); }
149};
150
151template <typename T> struct ISNANTy {
152 static constexpr RT_API_ATTRS bool compute(T x) { return std::isnan(x); }
153};
154
155template <typename T> struct LDEXPTy {
156 template <typename ET> static constexpr RT_API_ATTRS T compute(T x, ET e) {
157 return std::ldexp(x, e);
158 }
159};
160
161template <typename T> struct MAXTy {
162 static constexpr RT_API_ATTRS T compute() {
163 return std::numeric_limits<T>::max();
164 }
165};
166
167#if HAS_LDBL128 || HAS_FLOAT128
168template <> struct MAXTy<CppTypeFor<TypeCategory::Real, 16>> {
169 static CppTypeFor<TypeCategory::Real, 16> compute() {
171 }
172};
173#endif
174
175template <int PREC, typename T> struct MINTy {
176 static constexpr RT_API_ATTRS T compute() { return MinValue<PREC, T>::get(); }
177};
178
179template <typename T> struct QNANTy {
180 static constexpr RT_API_ATTRS T compute() {
181 return std::numeric_limits<T>::quiet_NaN();
182 }
183};
184
185template <typename T> struct SQRTTy {
186 static constexpr RT_API_ATTRS T compute(T x) { return std::sqrt(x); }
187};
188
189// EXPONENT (16.9.75)
190template <typename RESULT, typename ARG>
191inline RT_API_ATTRS RESULT Exponent(ARG x) {
193 return MAXTy<RESULT>::compute(); // +/-Inf, NaN -> HUGE(0)
194 } else if (x == 0) {
195 return 0; // 0 -> 0
196 } else {
197 return ILOGBTy<ARG>::compute(x) + 1;
198 }
199}
200
201// FRACTION (16.9.80)
202template <typename T> inline RT_API_ATTRS T Fraction(T x) {
203 if (ISNANTy<T>::compute(x)) {
204 return x; // NaN -> same NaN
205 } else if (ISINFTy<T>::compute(x)) {
206 return QNANTy<T>::compute(); // +/-Inf -> NaN
207 } else if (x == 0) {
208 return x; // 0 -> same 0
209 } else {
210 int ignoredExp;
211 return FREXPTy<T>::compute(x, &ignoredExp);
212 }
213}
214
215// SET_EXPONENT (16.9.171)
216template <typename T> inline RT_API_ATTRS T SetExponent(T x, std::int64_t p) {
217 if (ISNANTy<T>::compute(x)) {
218 return x; // NaN -> same NaN
219 } else if (ISINFTy<T>::compute(x)) {
220 return QNANTy<T>::compute(); // +/-Inf -> NaN
221 } else if (x == 0) {
222 return x; // return negative zero if x is negative zero
223 } else {
224 int expo{ILOGBTy<T>::compute(x) + 1};
225 auto ip{static_cast<int>(p - expo)};
226 if (ip != p - expo) {
227 ip = p < 0 ? std::numeric_limits<int>::min()
228 : std::numeric_limits<int>::max();
229 }
230 return LDEXPTy<T>::compute(x, ip); // x*2**(p-e)
231 }
232}
233
234// MOD & MODULO (16.9.135, .136)
235template <bool IS_MODULO, typename T>
236inline RT_API_ATTRS T RealMod(
237 T a, T p, const char *sourceFile, int sourceLine) {
238 if (p == 0) {
239 Terminator{sourceFile, sourceLine}.Crash(
240 IS_MODULO ? "MODULO with P==0" : "MOD with P==0");
241 }
242 if (ISNANTy<T>::compute(a) || ISNANTy<T>::compute(p) ||
243 ISINFTy<T>::compute(a)) {
244 return QNANTy<T>::compute();
245 } else if (IS_MODULO && ISINFTy<T>::compute(p)) {
246 // Other compilers behave consistently for MOD(x, +/-INF)
247 // and always return x. This is probably related to
248 // implementation of std::fmod(). Stick to this behavior
249 // for MOD, but return NaN for MODULO(x, +/-INF).
250 return QNANTy<T>::compute();
251 }
252 T aAbs{ABSTy<T>::compute(a)};
253 T pAbs{ABSTy<T>::compute(p)};
254 if (aAbs <= static_cast<T>(std::numeric_limits<std::int64_t>::max()) &&
255 pAbs <= static_cast<T>(std::numeric_limits<std::int64_t>::max())) {
256 if (auto aInt{static_cast<std::int64_t>(a)}; a == aInt) {
257 if (auto pInt{static_cast<std::int64_t>(p)}; p == pInt) {
258 // Fast exact case for integer operands
259 auto mod{aInt - (aInt / pInt) * pInt};
260 if constexpr (IS_MODULO) {
261 if (mod == 0) {
262 // Return properly signed zero.
263 return pInt > 0 ? T{0} : -T{0};
264 }
265 if ((aInt > 0) != (pInt > 0)) {
266 mod += pInt;
267 }
268 } else {
269 if (mod == 0) {
270 // Return properly signed zero.
271 return aInt > 0 ? T{0} : -T{0};
272 }
273 }
274 return static_cast<T>(mod);
275 }
276 }
277 }
278 if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double> ||
279 std::is_same_v<T, long double>) {
280 // std::fmod() semantics on signed operands seems to match
281 // the requirements of MOD(). MODULO() needs adjustment.
282 T result{std::fmod(a, p)};
283 if constexpr (IS_MODULO) {
284 if ((a < 0) != (p < 0)) {
285 if (result == 0.) {
286 result = -result;
287 } else {
288 result += p;
289 }
290 }
291 }
292 return result;
293 } else {
294 // The standard defines MOD(a,p)=a-AINT(a/p)*p and
295 // MODULO(a,p)=a-FLOOR(a/p)*p, but those definitions lose
296 // precision badly due to cancellation when ABS(a) is
297 // much larger than ABS(p).
298 // Insights:
299 // - MOD(a,p)=MOD(a-n*p,p) when a>0, p>0, integer n>0, and a>=n*p
300 // - when n is a power of two, n*p is exact
301 // - as a>=n*p, a-n*p does not round.
302 // So repeatedly reduce a by all n*p in decreasing order of n;
303 // what's left is the desired remainder. This is basically
304 // the same algorithm as arbitrary precision binary long division,
305 // discarding the quotient.
306 T tmp{aAbs};
307 for (T adj{SetExponent(pAbs, Exponent<int>(aAbs))}; tmp >= pAbs; adj /= 2) {
308 if (tmp >= adj) {
309 tmp -= adj;
310 if (tmp == 0) {
311 break;
312 }
313 }
314 }
315 if (a < 0) {
316 tmp = -tmp;
317 }
318 if constexpr (IS_MODULO) {
319 if ((a < 0) != (p < 0)) {
320 if (tmp == 0.) {
321 tmp = -tmp;
322 } else {
323 tmp += p;
324 }
325 }
326 }
327 return tmp;
328 }
329}
330
331// RRSPACING (16.9.164)
332template <int PREC, typename T> inline RT_API_ATTRS T RRSpacing(T x) {
333 if (ISNANTy<T>::compute(x)) {
334 return x; // NaN -> same NaN
335 } else if (ISINFTy<T>::compute(x)) {
336 return QNANTy<T>::compute(); // +/-Inf -> NaN
337 } else if (x == 0) {
338 return 0; // 0 -> 0
339 } else {
340 return LDEXPTy<T>::compute(
341 ABSTy<T>::compute(x), PREC - (ILOGBTy<T>::compute(x) + 1));
342 }
343}
344
345// SPACING (16.9.180)
346template <int PREC, typename T> inline RT_API_ATTRS T Spacing(T x) {
347 T tiny{MINTy<PREC, T>::compute()};
348 if (ISNANTy<T>::compute(x)) {
349 return x; // NaN -> same NaN
350 } else if (ISINFTy<T>::compute(x)) {
351 return QNANTy<T>::compute(); // +/-Inf -> NaN
352 } else if (x == 0) { // 0 -> TINY(x)
353 return tiny;
354 } else {
355 T result{LDEXPTy<T>::compute(
356 static_cast<T>(1.0), ILOGBTy<T>::compute(x) + 1 - PREC)}; // 2**(e-p)
357 // All compilers return TINY(x) for |x| <= TINY(x), but differ over whether
358 // SPACING(x) can be < TINY(x) for |x| > TINY(x). The most common precedent
359 // is to never return a value < TINY(x).
360 return result <= tiny ? tiny : result;
361 }
362}
363
364// ERFC_SCALED (16.9.71)
365template <typename T> inline RT_API_ATTRS T ErfcScaled(T arg) {
366 return common::ErfcScaled(arg);
367}
368
369} // namespace Fortran::runtime
370
371#endif // FORTRAN_RUNTIME_NUMERIC_TEMPLATES_H_
Definition: terminator.h:23
Definition: numeric-templates.h:126
Definition: numeric-templates.h:135
Definition: numeric-templates.h:143
Definition: numeric-templates.h:147
Definition: numeric-templates.h:151
Definition: numeric-templates.h:155
Definition: numeric-templates.h:161
Definition: numeric-templates.h:175
Definition: numeric-templates.h:35
Definition: numeric-templates.h:92
Definition: numeric-templates.h:179
Definition: numeric-templates.h:185