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