xref: /llvm-project/flang/unittests/Evaluate/real.cpp (revision fc97d2e68b03bc2979395e84b645e5b3ba35aecd)
1ee5fa1f2SLuke Ireland #include "fp-testing.h"
2ee5fa1f2SLuke Ireland #include "testing.h"
3ee5fa1f2SLuke Ireland #include "flang/Evaluate/type.h"
48670e499SCaroline Concatto #include "llvm/Support/raw_ostream.h"
5ee5fa1f2SLuke Ireland #include <cmath>
6ee5fa1f2SLuke Ireland #include <cstdio>
7ee5fa1f2SLuke Ireland #include <cstdlib>
8ee5fa1f2SLuke Ireland #include <type_traits>
9ee5fa1f2SLuke Ireland 
10ee5fa1f2SLuke Ireland using namespace Fortran::evaluate;
11ee5fa1f2SLuke Ireland using namespace Fortran::common;
12ee5fa1f2SLuke Ireland 
13ee5fa1f2SLuke Ireland using Real2 = Scalar<Type<TypeCategory::Real, 2>>;
14ee5fa1f2SLuke Ireland using Real3 = Scalar<Type<TypeCategory::Real, 3>>;
15ee5fa1f2SLuke Ireland using Real4 = Scalar<Type<TypeCategory::Real, 4>>;
16ee5fa1f2SLuke Ireland using Real8 = Scalar<Type<TypeCategory::Real, 8>>;
1758b51492Smadanial0 #ifdef __x86_64__
18ee5fa1f2SLuke Ireland using Real10 = Scalar<Type<TypeCategory::Real, 10>>;
1958b51492Smadanial0 #endif
20ee5fa1f2SLuke Ireland using Real16 = Scalar<Type<TypeCategory::Real, 16>>;
21ee5fa1f2SLuke Ireland using Integer4 = Scalar<Type<TypeCategory::Integer, 4>>;
22ee5fa1f2SLuke Ireland using Integer8 = Scalar<Type<TypeCategory::Integer, 8>>;
23ee5fa1f2SLuke Ireland 
24ee5fa1f2SLuke Ireland void dumpTest() {
25ee5fa1f2SLuke Ireland   struct {
26ee5fa1f2SLuke Ireland     std::uint64_t raw;
27ee5fa1f2SLuke Ireland     const char *expected;
28ee5fa1f2SLuke Ireland   } table[] = {
29ee5fa1f2SLuke Ireland       {0x7f876543, "NaN0x7f876543"},
30ee5fa1f2SLuke Ireland       {0x7f800000, "Inf"},
31ee5fa1f2SLuke Ireland       {0xff800000, "-Inf"},
32ee5fa1f2SLuke Ireland       {0x00000000, "0.0"},
33ee5fa1f2SLuke Ireland       {0x80000000, "-0.0"},
34ee5fa1f2SLuke Ireland       {0x3f800000, "0x1.0p0"},
35ee5fa1f2SLuke Ireland       {0xbf800000, "-0x1.0p0"},
36ee5fa1f2SLuke Ireland       {0x40000000, "0x1.0p1"},
37ee5fa1f2SLuke Ireland       {0x3f000000, "0x1.0p-1"},
38ee5fa1f2SLuke Ireland       {0x7f7fffff, "0x1.fffffep127"},
39ee5fa1f2SLuke Ireland       {0x00800000, "0x1.0p-126"},
400ff32224SPeixinQiao       {0x00400000, "0x0.8p-126"},
410ff32224SPeixinQiao       {0x00000001, "0x0.000002p-126"},
42ee5fa1f2SLuke Ireland       {0, nullptr},
43ee5fa1f2SLuke Ireland   };
44ee5fa1f2SLuke Ireland   for (int j{0}; table[j].expected != nullptr; ++j) {
45ee5fa1f2SLuke Ireland     TEST(Real4{Integer4{table[j].raw}}.DumpHexadecimal() == table[j].expected)
46ee5fa1f2SLuke Ireland     ("%d", j);
47ee5fa1f2SLuke Ireland   }
48ee5fa1f2SLuke Ireland }
49ee5fa1f2SLuke Ireland 
50ee5fa1f2SLuke Ireland template <typename R> void basicTests(int rm, Rounding rounding) {
51ee5fa1f2SLuke Ireland   static constexpr int kind{R::bits / 8};
52ee5fa1f2SLuke Ireland   char desc[64];
53ee5fa1f2SLuke Ireland   using Word = typename R::Word;
54ee5fa1f2SLuke Ireland   std::snprintf(desc, sizeof desc, "bits=%d, le=%d, kind=%d", R::bits,
55ee5fa1f2SLuke Ireland       Word::littleEndian, kind);
56ee5fa1f2SLuke Ireland   R zero;
57ee5fa1f2SLuke Ireland   TEST(!zero.IsNegative())(desc);
58ee5fa1f2SLuke Ireland   TEST(!zero.IsNotANumber())(desc);
59ee5fa1f2SLuke Ireland   TEST(!zero.IsInfinite())(desc);
60ee5fa1f2SLuke Ireland   TEST(zero.IsZero())(desc);
61ee5fa1f2SLuke Ireland   MATCH(0, zero.Exponent())(desc);
62ee5fa1f2SLuke Ireland   TEST(zero.RawBits().IsZero())(desc);
63ee5fa1f2SLuke Ireland   MATCH(0, zero.RawBits().ToUInt64())(desc);
64ee5fa1f2SLuke Ireland   TEST(zero.ABS().RawBits().IsZero())(desc);
65ee5fa1f2SLuke Ireland   TEST(zero.Negate().RawBits().IEOR(Word::MASKL(1)).IsZero())(desc);
66ee5fa1f2SLuke Ireland   TEST(zero.Compare(zero) == Relation::Equal)(desc);
67ee5fa1f2SLuke Ireland   R minusZero{Word{std::uint64_t{1}}.SHIFTL(R::bits - 1)};
68ee5fa1f2SLuke Ireland   TEST(minusZero.IsNegative())(desc);
69ee5fa1f2SLuke Ireland   TEST(!minusZero.IsNotANumber())(desc);
70ee5fa1f2SLuke Ireland   TEST(!minusZero.IsInfinite())(desc);
71ee5fa1f2SLuke Ireland   TEST(minusZero.IsZero())(desc);
72ee5fa1f2SLuke Ireland   TEST(minusZero.ABS().RawBits().IsZero())(desc);
73ee5fa1f2SLuke Ireland   TEST(minusZero.Negate().RawBits().IsZero())(desc);
74ee5fa1f2SLuke Ireland   MATCH(0, minusZero.Exponent())(desc);
75ee5fa1f2SLuke Ireland   MATCH(0, minusZero.RawBits().LEADZ())(desc);
76ee5fa1f2SLuke Ireland   MATCH(1, minusZero.RawBits().POPCNT())(desc);
77ee5fa1f2SLuke Ireland   TEST(minusZero.Compare(minusZero) == Relation::Equal)(desc);
78ee5fa1f2SLuke Ireland   TEST(zero.Compare(minusZero) == Relation::Equal)(desc);
79ee5fa1f2SLuke Ireland   ValueWithRealFlags<R> vr;
80ee5fa1f2SLuke Ireland   MATCH(0, vr.value.RawBits().ToUInt64())(desc);
81ee5fa1f2SLuke Ireland   TEST(vr.flags.empty())(desc);
82ee5fa1f2SLuke Ireland   R nan{Word{std::uint64_t{1}}
83ee5fa1f2SLuke Ireland             .SHIFTL(R::bits)
84ee5fa1f2SLuke Ireland             .SubtractSigned(Word{std::uint64_t{1}})
85ee5fa1f2SLuke Ireland             .value};
86ee5fa1f2SLuke Ireland   MATCH(R::bits, nan.RawBits().POPCNT())(desc);
87ee5fa1f2SLuke Ireland   TEST(!nan.IsNegative())(desc);
88ee5fa1f2SLuke Ireland   TEST(nan.IsNotANumber())(desc);
89ee5fa1f2SLuke Ireland   TEST(!nan.IsInfinite())(desc);
90ee5fa1f2SLuke Ireland   TEST(!nan.IsZero())(desc);
91ee5fa1f2SLuke Ireland   TEST(zero.Compare(nan) == Relation::Unordered)(desc);
92ee5fa1f2SLuke Ireland   TEST(minusZero.Compare(nan) == Relation::Unordered)(desc);
93ee5fa1f2SLuke Ireland   TEST(nan.Compare(zero) == Relation::Unordered)(desc);
94ee5fa1f2SLuke Ireland   TEST(nan.Compare(minusZero) == Relation::Unordered)(desc);
95ee5fa1f2SLuke Ireland   TEST(nan.Compare(nan) == Relation::Unordered)(desc);
96ee5fa1f2SLuke Ireland   int significandBits{R::binaryPrecision - R::isImplicitMSB};
97ee5fa1f2SLuke Ireland   int exponentBits{R::bits - significandBits - 1};
98ee5fa1f2SLuke Ireland   std::uint64_t maxExponent{(std::uint64_t{1} << exponentBits) - 1};
99ee5fa1f2SLuke Ireland   MATCH(nan.Exponent(), maxExponent)(desc);
1000e05ab67SPeter Klausler   Word infWord{Word{maxExponent}.SHIFTL(significandBits)};
1010e05ab67SPeter Klausler   Word negInfWord{
1020e05ab67SPeter Klausler       Word{maxExponent}.SHIFTL(significandBits).IOR(Word::MASKL(1))};
1030e05ab67SPeter Klausler   if constexpr (kind == 10) { // x87
1040e05ab67SPeter Klausler     infWord = infWord.IBSET(63);
1050e05ab67SPeter Klausler     negInfWord = negInfWord.IBSET(63);
1060e05ab67SPeter Klausler   }
1070e05ab67SPeter Klausler   R inf{infWord};
1080e05ab67SPeter Klausler   R negInf{negInfWord};
109ee5fa1f2SLuke Ireland   TEST(!inf.IsNegative())(desc);
110ee5fa1f2SLuke Ireland   TEST(!inf.IsNotANumber())(desc);
111ee5fa1f2SLuke Ireland   TEST(inf.IsInfinite())(desc);
112ee5fa1f2SLuke Ireland   TEST(!inf.IsZero())(desc);
113ee5fa1f2SLuke Ireland   TEST(inf.RawBits().CompareUnsigned(inf.ABS().RawBits()) == Ordering::Equal)
114ee5fa1f2SLuke Ireland   (desc);
115ee5fa1f2SLuke Ireland   TEST(zero.Compare(inf) == Relation::Less)(desc);
116ee5fa1f2SLuke Ireland   TEST(minusZero.Compare(inf) == Relation::Less)(desc);
117ee5fa1f2SLuke Ireland   TEST(nan.Compare(inf) == Relation::Unordered)(desc);
118ee5fa1f2SLuke Ireland   TEST(inf.Compare(inf) == Relation::Equal)(desc);
119ee5fa1f2SLuke Ireland   TEST(negInf.IsNegative())(desc);
120ee5fa1f2SLuke Ireland   TEST(!negInf.IsNotANumber())(desc);
121ee5fa1f2SLuke Ireland   TEST(negInf.IsInfinite())(desc);
122ee5fa1f2SLuke Ireland   TEST(!negInf.IsZero())(desc);
123ee5fa1f2SLuke Ireland   TEST(inf.RawBits().CompareUnsigned(negInf.ABS().RawBits()) == Ordering::Equal)
124ee5fa1f2SLuke Ireland   (desc);
125ee5fa1f2SLuke Ireland   TEST(inf.RawBits().CompareUnsigned(negInf.Negate().RawBits()) ==
126ee5fa1f2SLuke Ireland       Ordering::Equal)
127ee5fa1f2SLuke Ireland   (desc);
128ee5fa1f2SLuke Ireland   TEST(inf.Negate().RawBits().CompareUnsigned(negInf.RawBits()) ==
129ee5fa1f2SLuke Ireland       Ordering::Equal)
130ee5fa1f2SLuke Ireland   (desc);
131ee5fa1f2SLuke Ireland   TEST(zero.Compare(negInf) == Relation::Greater)(desc);
132ee5fa1f2SLuke Ireland   TEST(minusZero.Compare(negInf) == Relation::Greater)(desc);
133ee5fa1f2SLuke Ireland   TEST(nan.Compare(negInf) == Relation::Unordered)(desc);
134ee5fa1f2SLuke Ireland   TEST(inf.Compare(negInf) == Relation::Greater)(desc);
135ee5fa1f2SLuke Ireland   TEST(negInf.Compare(negInf) == Relation::Equal)(desc);
136ee5fa1f2SLuke Ireland   for (std::uint64_t j{0}; j < 63; ++j) {
137ee5fa1f2SLuke Ireland     char ldesc[128];
138ee5fa1f2SLuke Ireland     std::uint64_t x{1};
139ee5fa1f2SLuke Ireland     x <<= j;
140ee5fa1f2SLuke Ireland     std::snprintf(ldesc, sizeof ldesc, "%s j=%d x=0x%jx rm=%d", desc,
141ee5fa1f2SLuke Ireland         static_cast<int>(j), static_cast<std::intmax_t>(x), rm);
142ee5fa1f2SLuke Ireland     Integer8 ix{x};
143ee5fa1f2SLuke Ireland     TEST(!ix.IsNegative())(ldesc);
144ee5fa1f2SLuke Ireland     MATCH(x, ix.ToUInt64())(ldesc);
145*fc97d2e6SPeter Klausler     vr = R::FromInteger(ix, false, rounding);
146ee5fa1f2SLuke Ireland     TEST(!vr.value.IsNegative())(ldesc);
147ee5fa1f2SLuke Ireland     TEST(!vr.value.IsNotANumber())(ldesc);
148ee5fa1f2SLuke Ireland     TEST(!vr.value.IsZero())(ldesc);
149ee5fa1f2SLuke Ireland     auto ivf = vr.value.template ToInteger<Integer8>();
150ee5fa1f2SLuke Ireland     if (j > (maxExponent / 2)) {
151ee5fa1f2SLuke Ireland       TEST(vr.flags.test(RealFlag::Overflow))(ldesc);
152ee5fa1f2SLuke Ireland       TEST(vr.value.IsInfinite())(ldesc);
153ee5fa1f2SLuke Ireland       TEST(ivf.flags.test(RealFlag::Overflow))(ldesc);
154ee5fa1f2SLuke Ireland       MATCH(0x7fffffffffffffff, ivf.value.ToUInt64())(ldesc);
155ee5fa1f2SLuke Ireland     } else {
156ee5fa1f2SLuke Ireland       TEST(vr.flags.empty())(ldesc);
157ee5fa1f2SLuke Ireland       TEST(!vr.value.IsInfinite())(ldesc);
158ee5fa1f2SLuke Ireland       TEST(ivf.flags.empty())(ldesc);
159ee5fa1f2SLuke Ireland       MATCH(x, ivf.value.ToUInt64())(ldesc);
160ee5fa1f2SLuke Ireland       if (rounding.mode == RoundingMode::TiesToEven) { // to match stold()
161d5dd7d23SYoungsuk Kim         std::string decimal;
162d5dd7d23SYoungsuk Kim         llvm::raw_string_ostream ss{decimal};
163ee5fa1f2SLuke Ireland         vr.value.AsFortran(ss, kind, false /*exact*/);
164ee5fa1f2SLuke Ireland         const char *p{decimal.data()};
165ee5fa1f2SLuke Ireland         MATCH(x, static_cast<std::uint64_t>(std::stold(decimal)))
166ee5fa1f2SLuke Ireland         ("%s %s", ldesc, p);
167ee5fa1f2SLuke Ireland         auto check{R::Read(p, rounding)};
168ee5fa1f2SLuke Ireland         auto icheck{check.value.template ToInteger<Integer8>()};
169ee5fa1f2SLuke Ireland         MATCH(x, icheck.value.ToUInt64())(ldesc);
170ee5fa1f2SLuke Ireland         TEST(vr.value.Compare(check.value) == Relation::Equal)(ldesc);
171ee5fa1f2SLuke Ireland       }
172ee5fa1f2SLuke Ireland     }
173ee5fa1f2SLuke Ireland     TEST(vr.value.ToWholeNumber().value.Compare(vr.value) == Relation::Equal)
174ee5fa1f2SLuke Ireland     (ldesc);
175ee5fa1f2SLuke Ireland     ix = ix.Negate().value;
176ee5fa1f2SLuke Ireland     TEST(ix.IsNegative())(ldesc);
177ee5fa1f2SLuke Ireland     x = -x;
178ee5fa1f2SLuke Ireland     std::int64_t nx = x;
179ee5fa1f2SLuke Ireland     MATCH(x, ix.ToUInt64())(ldesc);
180ee5fa1f2SLuke Ireland     MATCH(nx, ix.ToInt64())(ldesc);
181ee5fa1f2SLuke Ireland     vr = R::FromInteger(ix);
182ee5fa1f2SLuke Ireland     TEST(vr.value.IsNegative())(ldesc);
183ee5fa1f2SLuke Ireland     TEST(!vr.value.IsNotANumber())(ldesc);
184ee5fa1f2SLuke Ireland     TEST(!vr.value.IsZero())(ldesc);
185ee5fa1f2SLuke Ireland     ivf = vr.value.template ToInteger<Integer8>();
186ee5fa1f2SLuke Ireland     if (j > (maxExponent / 2)) {
187ee5fa1f2SLuke Ireland       TEST(vr.flags.test(RealFlag::Overflow))(ldesc);
188ee5fa1f2SLuke Ireland       TEST(vr.value.IsInfinite())(ldesc);
189ee5fa1f2SLuke Ireland       TEST(ivf.flags.test(RealFlag::Overflow))(ldesc);
190ee5fa1f2SLuke Ireland       MATCH(0x8000000000000000, ivf.value.ToUInt64())(ldesc);
191ee5fa1f2SLuke Ireland     } else {
192ee5fa1f2SLuke Ireland       TEST(vr.flags.empty())(ldesc);
193ee5fa1f2SLuke Ireland       TEST(!vr.value.IsInfinite())(ldesc);
194ee5fa1f2SLuke Ireland       TEST(ivf.flags.empty())(ldesc);
195ee5fa1f2SLuke Ireland       MATCH(x, ivf.value.ToUInt64())(ldesc);
196ee5fa1f2SLuke Ireland       MATCH(nx, ivf.value.ToInt64())(ldesc);
197ee5fa1f2SLuke Ireland     }
198ee5fa1f2SLuke Ireland     TEST(vr.value.ToWholeNumber().value.Compare(vr.value) == Relation::Equal)
199ee5fa1f2SLuke Ireland     (ldesc);
200ee5fa1f2SLuke Ireland   }
201ee5fa1f2SLuke Ireland }
202ee5fa1f2SLuke Ireland 
203ee5fa1f2SLuke Ireland // Takes an integer and distributes its bits across a floating
204ee5fa1f2SLuke Ireland // point value.  The LSB is used to complement the result.
205ee5fa1f2SLuke Ireland std::uint32_t MakeReal(std::uint32_t n) {
206ee5fa1f2SLuke Ireland   int shifts[] = {-1, 31, 23, 30, 22, 0, 24, 29, 25, 28, 26, 1, 16, 21, 2, -1};
207ee5fa1f2SLuke Ireland   std::uint32_t x{0};
208ee5fa1f2SLuke Ireland   for (int j{1}; shifts[j] >= 0; ++j) {
209ee5fa1f2SLuke Ireland     x |= ((n >> j) & 1) << shifts[j];
210ee5fa1f2SLuke Ireland   }
211ee5fa1f2SLuke Ireland   x ^= -(n & 1);
212ee5fa1f2SLuke Ireland   return x;
213ee5fa1f2SLuke Ireland }
214ee5fa1f2SLuke Ireland 
215ee5fa1f2SLuke Ireland std::uint64_t MakeReal(std::uint64_t n) {
216ee5fa1f2SLuke Ireland   int shifts[] = {
217ee5fa1f2SLuke Ireland       -1, 63, 52, 62, 51, 0, 53, 61, 54, 60, 55, 59, 1, 16, 50, 2, -1};
218ee5fa1f2SLuke Ireland   std::uint64_t x{0};
219ee5fa1f2SLuke Ireland   for (int j{1}; shifts[j] >= 0; ++j) {
220ee5fa1f2SLuke Ireland     x |= ((n >> j) & 1) << shifts[j];
221ee5fa1f2SLuke Ireland   }
222ee5fa1f2SLuke Ireland   x ^= -(n & 1);
223ee5fa1f2SLuke Ireland   return x;
224ee5fa1f2SLuke Ireland }
225ee5fa1f2SLuke Ireland 
226ee5fa1f2SLuke Ireland inline bool IsNaN(std::uint32_t x) {
227ee5fa1f2SLuke Ireland   return (x & 0x7f800000) == 0x7f800000 && (x & 0x007fffff) != 0;
228ee5fa1f2SLuke Ireland }
229ee5fa1f2SLuke Ireland 
230ee5fa1f2SLuke Ireland inline bool IsNaN(std::uint64_t x) {
231ee5fa1f2SLuke Ireland   return (x & 0x7ff0000000000000) == 0x7ff0000000000000 &&
232ee5fa1f2SLuke Ireland       (x & 0x000fffffffffffff) != 0;
233ee5fa1f2SLuke Ireland }
234ee5fa1f2SLuke Ireland 
235ee5fa1f2SLuke Ireland inline bool IsInfinite(std::uint32_t x) {
236ee5fa1f2SLuke Ireland   return (x & 0x7fffffff) == 0x7f800000;
237ee5fa1f2SLuke Ireland }
238ee5fa1f2SLuke Ireland 
239ee5fa1f2SLuke Ireland inline bool IsInfinite(std::uint64_t x) {
240ee5fa1f2SLuke Ireland   return (x & 0x7fffffffffffffff) == 0x7ff0000000000000;
241ee5fa1f2SLuke Ireland }
242ee5fa1f2SLuke Ireland 
243ee5fa1f2SLuke Ireland inline bool IsNegative(std::uint32_t x) { return (x & 0x80000000) != 0; }
244ee5fa1f2SLuke Ireland 
245ee5fa1f2SLuke Ireland inline bool IsNegative(std::uint64_t x) {
246ee5fa1f2SLuke Ireland   return (x & 0x8000000000000000) != 0;
247ee5fa1f2SLuke Ireland }
248ee5fa1f2SLuke Ireland 
249ee5fa1f2SLuke Ireland inline std::uint32_t NormalizeNaN(std::uint32_t x) {
250ee5fa1f2SLuke Ireland   if (IsNaN(x)) {
251ee5fa1f2SLuke Ireland     x = 0x7fe00000;
252ee5fa1f2SLuke Ireland   }
253ee5fa1f2SLuke Ireland   return x;
254ee5fa1f2SLuke Ireland }
255ee5fa1f2SLuke Ireland 
256ee5fa1f2SLuke Ireland inline std::uint64_t NormalizeNaN(std::uint64_t x) {
257ee5fa1f2SLuke Ireland   if (IsNaN(x)) {
258ee5fa1f2SLuke Ireland     x = 0x7ffc000000000000;
259ee5fa1f2SLuke Ireland   }
260ee5fa1f2SLuke Ireland   return x;
261ee5fa1f2SLuke Ireland }
262ee5fa1f2SLuke Ireland 
263ee5fa1f2SLuke Ireland enum FlagBits {
264ee5fa1f2SLuke Ireland   Overflow = 1,
265ee5fa1f2SLuke Ireland   DivideByZero = 2,
266ee5fa1f2SLuke Ireland   InvalidArgument = 4,
267ee5fa1f2SLuke Ireland   Underflow = 8,
268ee5fa1f2SLuke Ireland   Inexact = 16,
269ee5fa1f2SLuke Ireland };
270ee5fa1f2SLuke Ireland 
271ee5fa1f2SLuke Ireland #ifdef __clang__
272ee5fa1f2SLuke Ireland // clang support for fenv.h is broken, so tests of flag settings
273ee5fa1f2SLuke Ireland // are disabled.
274ee5fa1f2SLuke Ireland inline std::uint32_t FlagsToBits(const RealFlags &) { return 0; }
275ee5fa1f2SLuke Ireland #else
276ee5fa1f2SLuke Ireland inline std::uint32_t FlagsToBits(const RealFlags &flags) {
277ee5fa1f2SLuke Ireland   std::uint32_t bits{0};
278ee5fa1f2SLuke Ireland   if (flags.test(RealFlag::Overflow)) {
279ee5fa1f2SLuke Ireland     bits |= Overflow;
280ee5fa1f2SLuke Ireland   }
281ee5fa1f2SLuke Ireland   if (flags.test(RealFlag::DivideByZero)) {
282ee5fa1f2SLuke Ireland     bits |= DivideByZero;
283ee5fa1f2SLuke Ireland   }
284ee5fa1f2SLuke Ireland   if (flags.test(RealFlag::InvalidArgument)) {
285ee5fa1f2SLuke Ireland     bits |= InvalidArgument;
286ee5fa1f2SLuke Ireland   }
287ee5fa1f2SLuke Ireland   if (flags.test(RealFlag::Underflow)) {
288ee5fa1f2SLuke Ireland     bits |= Underflow;
289ee5fa1f2SLuke Ireland   }
290ee5fa1f2SLuke Ireland   if (flags.test(RealFlag::Inexact)) {
291ee5fa1f2SLuke Ireland     bits |= Inexact;
292ee5fa1f2SLuke Ireland   }
293ee5fa1f2SLuke Ireland   return bits;
294ee5fa1f2SLuke Ireland }
295ee5fa1f2SLuke Ireland #endif // __clang__
296ee5fa1f2SLuke Ireland 
297ee5fa1f2SLuke Ireland template <typename UINT = std::uint32_t, typename FLT = float, typename REAL>
298ee5fa1f2SLuke Ireland void inttest(std::int64_t x, int pass, Rounding rounding) {
299ee5fa1f2SLuke Ireland   union {
300ee5fa1f2SLuke Ireland     UINT ui;
301ee5fa1f2SLuke Ireland     FLT f;
302ee5fa1f2SLuke Ireland   } u;
303ee5fa1f2SLuke Ireland   ScopedHostFloatingPointEnvironment fpenv;
304ee5fa1f2SLuke Ireland   Integer8 ix{x};
305ee5fa1f2SLuke Ireland   ValueWithRealFlags<REAL> real;
306*fc97d2e6SPeter Klausler   real = real.value.FromInteger(ix, false, rounding);
307ee5fa1f2SLuke Ireland #ifndef __clang__ // broken and also slow
308ee5fa1f2SLuke Ireland   fpenv.ClearFlags();
309ee5fa1f2SLuke Ireland #endif
310ee5fa1f2SLuke Ireland   FLT fcheck = x; // TODO unsigned too
311ee5fa1f2SLuke Ireland   auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
312ee5fa1f2SLuke Ireland   u.f = fcheck;
313ee5fa1f2SLuke Ireland   UINT rcheck{NormalizeNaN(u.ui)};
314ee5fa1f2SLuke Ireland   UINT check = real.value.RawBits().ToUInt64();
315ee5fa1f2SLuke Ireland   MATCH(rcheck, check)("%d 0x%llx", pass, x);
316ee5fa1f2SLuke Ireland   MATCH(actualFlags, FlagsToBits(real.flags))("%d 0x%llx", pass, x);
317ee5fa1f2SLuke Ireland }
318ee5fa1f2SLuke Ireland 
319ee5fa1f2SLuke Ireland template <typename FLT = float> FLT ToIntPower(FLT x, int power) {
320ee5fa1f2SLuke Ireland   if (power == 0) {
321ee5fa1f2SLuke Ireland     return x / x;
322ee5fa1f2SLuke Ireland   }
323ee5fa1f2SLuke Ireland   bool negative{power < 0};
324ee5fa1f2SLuke Ireland   if (negative) {
325ee5fa1f2SLuke Ireland     power = -power;
326ee5fa1f2SLuke Ireland   }
327ee5fa1f2SLuke Ireland   FLT result{1};
328ee5fa1f2SLuke Ireland   while (power > 0) {
329ee5fa1f2SLuke Ireland     if (power & 1) {
330ee5fa1f2SLuke Ireland       result *= x;
331ee5fa1f2SLuke Ireland     }
332ee5fa1f2SLuke Ireland     x *= x;
333ee5fa1f2SLuke Ireland     power >>= 1;
334ee5fa1f2SLuke Ireland   }
335ee5fa1f2SLuke Ireland   if (negative) {
336ee5fa1f2SLuke Ireland     result = 1.0 / result;
337ee5fa1f2SLuke Ireland   }
338ee5fa1f2SLuke Ireland   return result;
339ee5fa1f2SLuke Ireland }
340ee5fa1f2SLuke Ireland 
341ee5fa1f2SLuke Ireland template <typename FLT, int decimalDigits>
342ee5fa1f2SLuke Ireland FLT TimesIntPowerOfTen(FLT x, int power) {
343ee5fa1f2SLuke Ireland   if (power > decimalDigits || power < -decimalDigits) {
344ee5fa1f2SLuke Ireland     auto maxExactPowerOfTen{
345ee5fa1f2SLuke Ireland         TimesIntPowerOfTen<FLT, decimalDigits>(1, decimalDigits)};
346ee5fa1f2SLuke Ireland     auto big{ToIntPower<FLT>(maxExactPowerOfTen, power / decimalDigits)};
347ee5fa1f2SLuke Ireland     auto small{
348ee5fa1f2SLuke Ireland         TimesIntPowerOfTen<FLT, decimalDigits>(1, power % decimalDigits)};
349ee5fa1f2SLuke Ireland     return (x * big) * small;
350ee5fa1f2SLuke Ireland   }
351ee5fa1f2SLuke Ireland   return x * ToIntPower<FLT>(10.0, power);
352ee5fa1f2SLuke Ireland }
353ee5fa1f2SLuke Ireland 
354ee5fa1f2SLuke Ireland template <typename UINT = std::uint32_t, typename FLT = float,
355ee5fa1f2SLuke Ireland     typename REAL = Real4>
356ee5fa1f2SLuke Ireland void subsetTests(int pass, Rounding rounding, std::uint32_t opds) {
357ee5fa1f2SLuke Ireland   for (int j{0}; j < 63; ++j) {
358ee5fa1f2SLuke Ireland     std::int64_t x{1};
359ee5fa1f2SLuke Ireland     x <<= j;
360ee5fa1f2SLuke Ireland     inttest<UINT, FLT, REAL>(x, pass, rounding);
361ee5fa1f2SLuke Ireland     inttest<UINT, FLT, REAL>(-x, pass, rounding);
362ee5fa1f2SLuke Ireland   }
363ee5fa1f2SLuke Ireland   inttest<UINT, FLT, REAL>(0, pass, rounding);
364ee5fa1f2SLuke Ireland   inttest<UINT, FLT, REAL>(
365ee5fa1f2SLuke Ireland       static_cast<std::int64_t>(0x8000000000000000), pass, rounding);
366ee5fa1f2SLuke Ireland 
367ee5fa1f2SLuke Ireland   union {
368ee5fa1f2SLuke Ireland     UINT ui;
369ee5fa1f2SLuke Ireland     FLT f;
370ee5fa1f2SLuke Ireland   } u;
371ee5fa1f2SLuke Ireland   ScopedHostFloatingPointEnvironment fpenv;
372ee5fa1f2SLuke Ireland 
373ee5fa1f2SLuke Ireland   for (UINT j{0}; j < opds; ++j) {
374ee5fa1f2SLuke Ireland 
375ee5fa1f2SLuke Ireland     UINT rj{MakeReal(j)};
376ee5fa1f2SLuke Ireland     u.ui = rj;
377ee5fa1f2SLuke Ireland     FLT fj{u.f};
378ee5fa1f2SLuke Ireland     REAL x{typename REAL::Word{std::uint64_t{rj}}};
379ee5fa1f2SLuke Ireland 
380ee5fa1f2SLuke Ireland     // unary operations
381ee5fa1f2SLuke Ireland     {
382ee5fa1f2SLuke Ireland       ValueWithRealFlags<REAL> aint{x.ToWholeNumber()};
383ee5fa1f2SLuke Ireland #ifndef __clang__ // broken and also slow
384ee5fa1f2SLuke Ireland       fpenv.ClearFlags();
385ee5fa1f2SLuke Ireland #endif
386ee5fa1f2SLuke Ireland       FLT fcheck{std::trunc(fj)};
387ee5fa1f2SLuke Ireland       auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
388ee5fa1f2SLuke Ireland       actualFlags &= ~Inexact; // x86 std::trunc can set Inexact; AINT ain't
389ee5fa1f2SLuke Ireland       u.f = fcheck;
390ee5fa1f2SLuke Ireland #ifndef __clang__
391ee5fa1f2SLuke Ireland       if (IsNaN(u.ui)) {
392ee5fa1f2SLuke Ireland         actualFlags |= InvalidArgument; // x86 std::trunc(NaN) workaround
393ee5fa1f2SLuke Ireland       }
394ee5fa1f2SLuke Ireland #endif
395ee5fa1f2SLuke Ireland       UINT rcheck{NormalizeNaN(u.ui)};
396ee5fa1f2SLuke Ireland       UINT check = aint.value.RawBits().ToUInt64();
397ee5fa1f2SLuke Ireland       MATCH(rcheck, check)
398ee5fa1f2SLuke Ireland       ("%d AINT(0x%jx)", pass, static_cast<std::intmax_t>(rj));
399ee5fa1f2SLuke Ireland       MATCH(actualFlags, FlagsToBits(aint.flags))
400ee5fa1f2SLuke Ireland       ("%d AINT(0x%jx)", pass, static_cast<std::intmax_t>(rj));
401ee5fa1f2SLuke Ireland     }
402ee5fa1f2SLuke Ireland 
403ee5fa1f2SLuke Ireland     {
4041ef5e6deSPeter Klausler       ValueWithRealFlags<REAL> root{x.SQRT(rounding)};
4051ef5e6deSPeter Klausler #ifndef __clang__ // broken and also slow
4061ef5e6deSPeter Klausler       fpenv.ClearFlags();
4071ef5e6deSPeter Klausler #endif
4081ef5e6deSPeter Klausler       FLT fcheck{std::sqrt(fj)};
4091ef5e6deSPeter Klausler       auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
4101ef5e6deSPeter Klausler       u.f = fcheck;
4111ef5e6deSPeter Klausler       UINT rcheck{NormalizeNaN(u.ui)};
4121ef5e6deSPeter Klausler       UINT check = root.value.RawBits().ToUInt64();
4131ef5e6deSPeter Klausler       MATCH(rcheck, check)
4141ef5e6deSPeter Klausler       ("%d SQRT(0x%jx)", pass, static_cast<std::intmax_t>(rj));
4151ef5e6deSPeter Klausler       MATCH(actualFlags, FlagsToBits(root.flags))
4161ef5e6deSPeter Klausler       ("%d SQRT(0x%jx)", pass, static_cast<std::intmax_t>(rj));
4171ef5e6deSPeter Klausler     }
4181ef5e6deSPeter Klausler 
4191ef5e6deSPeter Klausler     {
420ee5fa1f2SLuke Ireland       MATCH(IsNaN(rj), x.IsNotANumber())
421ee5fa1f2SLuke Ireland       ("%d IsNaN(0x%jx)", pass, static_cast<std::intmax_t>(rj));
422ee5fa1f2SLuke Ireland       MATCH(IsInfinite(rj), x.IsInfinite())
423ee5fa1f2SLuke Ireland       ("%d IsInfinite(0x%jx)", pass, static_cast<std::intmax_t>(rj));
424ee5fa1f2SLuke Ireland 
425ee5fa1f2SLuke Ireland       static constexpr int kind{REAL::bits / 8};
426d5dd7d23SYoungsuk Kim       std::string s, cssBuf;
427d5dd7d23SYoungsuk Kim       llvm::raw_string_ostream ss{s};
4288670e499SCaroline Concatto       llvm::raw_string_ostream css{cssBuf};
429ee5fa1f2SLuke Ireland       x.AsFortran(ss, kind, false /*exact*/);
430ee5fa1f2SLuke Ireland       if (IsNaN(rj)) {
431ee5fa1f2SLuke Ireland         css << "(0._" << kind << "/0.)";
432d5dd7d23SYoungsuk Kim         MATCH(cssBuf, s)
433ee5fa1f2SLuke Ireland         ("%d invalid(0x%jx)", pass, static_cast<std::intmax_t>(rj));
434ee5fa1f2SLuke Ireland       } else if (IsInfinite(rj)) {
435ee5fa1f2SLuke Ireland         css << '(';
436ee5fa1f2SLuke Ireland         if (IsNegative(rj)) {
437ee5fa1f2SLuke Ireland           css << '-';
438ee5fa1f2SLuke Ireland         }
439ee5fa1f2SLuke Ireland         css << "1._" << kind << "/0.)";
440d5dd7d23SYoungsuk Kim         MATCH(cssBuf, s)
441ee5fa1f2SLuke Ireland         ("%d overflow(0x%jx)", pass, static_cast<std::intmax_t>(rj));
442ee5fa1f2SLuke Ireland       } else {
443ee5fa1f2SLuke Ireland         const char *p = s.data();
444ee5fa1f2SLuke Ireland         if (*p == '(') {
445ee5fa1f2SLuke Ireland           ++p;
446ee5fa1f2SLuke Ireland         }
447ee5fa1f2SLuke Ireland         auto readBack{REAL::Read(p, rounding)};
448ee5fa1f2SLuke Ireland         MATCH(rj, readBack.value.RawBits().ToUInt64())
449ee5fa1f2SLuke Ireland         ("%d Read(AsFortran()) 0x%jx %s %g", pass,
450ee5fa1f2SLuke Ireland             static_cast<std::intmax_t>(rj), s.data(), static_cast<double>(fj));
451ee5fa1f2SLuke Ireland         MATCH('_', *p)
452ee5fa1f2SLuke Ireland         ("%d Read(AsFortran()) 0x%jx %s %d", pass,
453ee5fa1f2SLuke Ireland             static_cast<std::intmax_t>(rj), s.data(),
454ee5fa1f2SLuke Ireland             static_cast<int>(p - s.data()));
455ee5fa1f2SLuke Ireland       }
456ee5fa1f2SLuke Ireland     }
457ee5fa1f2SLuke Ireland 
458ee5fa1f2SLuke Ireland     // dyadic operations
459ee5fa1f2SLuke Ireland     for (UINT k{0}; k < opds; ++k) {
460ee5fa1f2SLuke Ireland       UINT rk{MakeReal(k)};
461ee5fa1f2SLuke Ireland       u.ui = rk;
462ee5fa1f2SLuke Ireland       FLT fk{u.f};
463ee5fa1f2SLuke Ireland       REAL y{typename REAL::Word{std::uint64_t{rk}}};
464ee5fa1f2SLuke Ireland       {
465ee5fa1f2SLuke Ireland         ValueWithRealFlags<REAL> sum{x.Add(y, rounding)};
466ee5fa1f2SLuke Ireland #ifndef __clang__ // broken and also slow
467ee5fa1f2SLuke Ireland         fpenv.ClearFlags();
468ee5fa1f2SLuke Ireland #endif
469ee5fa1f2SLuke Ireland         FLT fcheck{fj + fk};
470ee5fa1f2SLuke Ireland         auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
471ee5fa1f2SLuke Ireland         u.f = fcheck;
472ee5fa1f2SLuke Ireland         UINT rcheck{NormalizeNaN(u.ui)};
473ee5fa1f2SLuke Ireland         UINT check = sum.value.RawBits().ToUInt64();
474ee5fa1f2SLuke Ireland         MATCH(rcheck, check)
475ee5fa1f2SLuke Ireland         ("%d 0x%jx + 0x%jx", pass, static_cast<std::intmax_t>(rj),
476ee5fa1f2SLuke Ireland             static_cast<std::intmax_t>(rk));
477ee5fa1f2SLuke Ireland         MATCH(actualFlags, FlagsToBits(sum.flags))
478ee5fa1f2SLuke Ireland         ("%d 0x%jx + 0x%jx", pass, static_cast<std::intmax_t>(rj),
479ee5fa1f2SLuke Ireland             static_cast<std::intmax_t>(rk));
480ee5fa1f2SLuke Ireland       }
481ee5fa1f2SLuke Ireland       {
482ee5fa1f2SLuke Ireland         ValueWithRealFlags<REAL> diff{x.Subtract(y, rounding)};
483ee5fa1f2SLuke Ireland #ifndef __clang__ // broken and also slow
484ee5fa1f2SLuke Ireland         fpenv.ClearFlags();
485ee5fa1f2SLuke Ireland #endif
486ee5fa1f2SLuke Ireland         FLT fcheck{fj - fk};
487ee5fa1f2SLuke Ireland         auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
488ee5fa1f2SLuke Ireland         u.f = fcheck;
489ee5fa1f2SLuke Ireland         UINT rcheck{NormalizeNaN(u.ui)};
490ee5fa1f2SLuke Ireland         UINT check = diff.value.RawBits().ToUInt64();
491ee5fa1f2SLuke Ireland         MATCH(rcheck, check)
492ee5fa1f2SLuke Ireland         ("%d 0x%jx - 0x%jx", pass, static_cast<std::intmax_t>(rj),
493ee5fa1f2SLuke Ireland             static_cast<std::intmax_t>(rk));
494ee5fa1f2SLuke Ireland         MATCH(actualFlags, FlagsToBits(diff.flags))
495ee5fa1f2SLuke Ireland         ("%d 0x%jx - 0x%jx", pass, static_cast<std::intmax_t>(rj),
496ee5fa1f2SLuke Ireland             static_cast<std::intmax_t>(rk));
497ee5fa1f2SLuke Ireland       }
498ee5fa1f2SLuke Ireland       {
499ee5fa1f2SLuke Ireland         ValueWithRealFlags<REAL> prod{x.Multiply(y, rounding)};
500ee5fa1f2SLuke Ireland #ifndef __clang__ // broken and also slow
501ee5fa1f2SLuke Ireland         fpenv.ClearFlags();
502ee5fa1f2SLuke Ireland #endif
503ee5fa1f2SLuke Ireland         FLT fcheck{fj * fk};
504ee5fa1f2SLuke Ireland         auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
505ee5fa1f2SLuke Ireland         u.f = fcheck;
506ee5fa1f2SLuke Ireland         UINT rcheck{NormalizeNaN(u.ui)};
507ee5fa1f2SLuke Ireland         UINT check = prod.value.RawBits().ToUInt64();
508ee5fa1f2SLuke Ireland         MATCH(rcheck, check)
509ee5fa1f2SLuke Ireland         ("%d 0x%jx * 0x%jx", pass, static_cast<std::intmax_t>(rj),
510ee5fa1f2SLuke Ireland             static_cast<std::intmax_t>(rk));
511ee5fa1f2SLuke Ireland         MATCH(actualFlags, FlagsToBits(prod.flags))
512ee5fa1f2SLuke Ireland         ("%d 0x%jx * 0x%jx", pass, static_cast<std::intmax_t>(rj),
513ee5fa1f2SLuke Ireland             static_cast<std::intmax_t>(rk));
514ee5fa1f2SLuke Ireland       }
515ee5fa1f2SLuke Ireland       {
516ee5fa1f2SLuke Ireland         ValueWithRealFlags<REAL> quot{x.Divide(y, rounding)};
517ee5fa1f2SLuke Ireland #ifndef __clang__ // broken and also slow
518ee5fa1f2SLuke Ireland         fpenv.ClearFlags();
519ee5fa1f2SLuke Ireland #endif
520ee5fa1f2SLuke Ireland         FLT fcheck{fj / fk};
521ee5fa1f2SLuke Ireland         auto actualFlags{FlagsToBits(fpenv.CurrentFlags())};
522ee5fa1f2SLuke Ireland         u.f = fcheck;
523ee5fa1f2SLuke Ireland         UINT rcheck{NormalizeNaN(u.ui)};
524ee5fa1f2SLuke Ireland         UINT check = quot.value.RawBits().ToUInt64();
525ee5fa1f2SLuke Ireland         MATCH(rcheck, check)
526ee5fa1f2SLuke Ireland         ("%d 0x%jx / 0x%jx", pass, static_cast<std::intmax_t>(rj),
527ee5fa1f2SLuke Ireland             static_cast<std::intmax_t>(rk));
528ee5fa1f2SLuke Ireland         MATCH(actualFlags, FlagsToBits(quot.flags))
529ee5fa1f2SLuke Ireland         ("%d 0x%jx / 0x%jx", pass, static_cast<std::intmax_t>(rj),
530ee5fa1f2SLuke Ireland             static_cast<std::intmax_t>(rk));
531ee5fa1f2SLuke Ireland       }
532ee5fa1f2SLuke Ireland     }
533ee5fa1f2SLuke Ireland   }
534ee5fa1f2SLuke Ireland }
535ee5fa1f2SLuke Ireland 
536ee5fa1f2SLuke Ireland void roundTest(int rm, Rounding rounding, std::uint32_t opds) {
537ee5fa1f2SLuke Ireland   basicTests<Real2>(rm, rounding);
538ee5fa1f2SLuke Ireland   basicTests<Real3>(rm, rounding);
539ee5fa1f2SLuke Ireland   basicTests<Real4>(rm, rounding);
540ee5fa1f2SLuke Ireland   basicTests<Real8>(rm, rounding);
54158b51492Smadanial0 #ifdef __x86_64__
542ee5fa1f2SLuke Ireland   basicTests<Real10>(rm, rounding);
54358b51492Smadanial0 #endif
544ee5fa1f2SLuke Ireland   basicTests<Real16>(rm, rounding);
545ee5fa1f2SLuke Ireland   ScopedHostFloatingPointEnvironment::SetRounding(rounding);
546ee5fa1f2SLuke Ireland   subsetTests<std::uint32_t, float, Real4>(rm, rounding, opds);
547ee5fa1f2SLuke Ireland   subsetTests<std::uint64_t, double, Real8>(rm, rounding, opds);
548ee5fa1f2SLuke Ireland }
549ee5fa1f2SLuke Ireland 
550ee5fa1f2SLuke Ireland int main() {
551ee5fa1f2SLuke Ireland   dumpTest();
552ee5fa1f2SLuke Ireland   std::uint32_t opds{512}; // for quick testing by default
553ee5fa1f2SLuke Ireland   if (const char *p{std::getenv("REAL_TEST_OPERANDS")}) {
554ee5fa1f2SLuke Ireland     // Use 8192 or 16384 for more exhaustive testing.
555ee5fa1f2SLuke Ireland     opds = std::atol(p);
556ee5fa1f2SLuke Ireland   }
557ee5fa1f2SLuke Ireland   roundTest(0, Rounding{RoundingMode::TiesToEven}, opds);
558ee5fa1f2SLuke Ireland   roundTest(1, Rounding{RoundingMode::ToZero}, opds);
559ee5fa1f2SLuke Ireland   roundTest(2, Rounding{RoundingMode::Up}, opds);
560ee5fa1f2SLuke Ireland   roundTest(3, Rounding{RoundingMode::Down}, opds);
561ee5fa1f2SLuke Ireland   // TODO: how to test Rounding::TiesAwayFromZero on x86?
562ee5fa1f2SLuke Ireland   return testing::Complete();
563ee5fa1f2SLuke Ireland }
564