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