1 // integer_test_suite.hpp : arithmetic test suite for abitrary precision integers 2 // 3 // Copyright (C) 2017-2021 Stillwater Supercomputing, Inc. 4 // 5 // This file is part of the universal numbers project, which is released under an MIT Open Source license. 6 #include <iostream> 7 #include <string> 8 9 // the integer number class will be configured outside of this helper 10 // 11 /* 12 The goal of the arbitrary integers is to provide a constrained big integer type 13 that enables fast computation with exceptions for overflow, so that the type 14 can be used for forward error analysis studies. 15 */ 16 #include <universal/verification/test_status.hpp> // ReportTestResult used by test suite runner 17 #include <universal/verification/test_reporters.hpp> 18 19 namespace sw::universal { 20 21 // enumerate all addition cases for an integer<16> configuration compared against native short 22 template<typename BlockType, size_t testBits = 12> VerifyShortAddition(bool bReportIndividualTestCases)23 int VerifyShortAddition(bool bReportIndividualTestCases) { 24 constexpr size_t nbits = 16; 25 constexpr size_t NR_INTEGERS = (size_t(1) << testBits); 26 27 using Integer = integer<nbits, BlockType>; 28 Integer ia, ib, iresult, iref; 29 30 int nrOfFailedTests = 0; 31 for (size_t i = 0; i < NR_INTEGERS; i++) { 32 ia.setbits(i); 33 short i64a = short(ia); 34 for (size_t j = 0; j < NR_INTEGERS; j++) { 35 ib.setbits(j); 36 short i64b = short(ib); 37 iref = i64a + i64b; 38 #if INTEGER_THROW_ARITHMETIC_EXCEPTION 39 try { 40 iresult = ia + ib; 41 } 42 catch (...) { 43 Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg); 44 if (iref > max_int || iref < min_int) { 45 // correctly caught the exception 46 continue; 47 } 48 else { 49 nrOfFailedTests++; 50 } 51 } 52 53 #else 54 iresult = ia + ib; 55 #endif 56 if (iresult != iref) { 57 nrOfFailedTests++; 58 if (bReportIndividualTestCases) ReportBinaryArithmeticError("FAIL", "+", ia, ib, iref, iresult); 59 } 60 else { 61 //if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "+", ia, ib, iref, iresult); 62 } 63 } 64 if (i % 1024 == 0) std::cout << '.'; 65 } 66 std::cout << std::endl; 67 68 return nrOfFailedTests; 69 } 70 // enumerate all subtraction cases for an integer<16> configuration compared against native short 71 template<typename BlockType, size_t testBits = 12> VerifyShortSubtraction(bool bReportIndividualTestCases)72 int VerifyShortSubtraction(bool bReportIndividualTestCases) { 73 constexpr size_t nbits = 16; 74 constexpr size_t NR_INTEGERS = (size_t(1) << testBits); 75 76 using Integer = integer<nbits, BlockType>; 77 Integer ia, ib, iresult, iref; 78 79 int nrOfFailedTests = 0; 80 for (size_t i = 0; i < NR_INTEGERS; i++) { 81 ia.setbits(i); 82 short i16a = short(ia); 83 for (size_t j = 0; j < NR_INTEGERS; j++) { 84 ib.setbits(j); 85 short i16b = short(ib); 86 iref = i16a - i16b; 87 #if INTEGER_THROW_ARITHMETIC_EXCEPTION 88 try { 89 iresult = ia - ib; 90 } 91 catch (...) { 92 Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg); 93 if (iref > max_int || iref < min_int) { 94 // correctly caught the exception 95 continue; 96 } 97 else { 98 nrOfFailedTests++; 99 } 100 } 101 102 #else 103 iresult = ia - ib; 104 #endif 105 if (iresult != iref) { 106 nrOfFailedTests++; 107 if (bReportIndividualTestCases) ReportBinaryArithmeticError("FAIL", "-", ia, ib, iref, iresult); 108 } 109 else { 110 //if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "-", ia, ib, iref, iresult); 111 } 112 } 113 if (i % 1024 == 0) std::cout << '.'; 114 } 115 std::cout << std::endl; 116 117 return nrOfFailedTests; 118 } 119 // enumerate all multiplication cases for an integer<16> configuration compared against native short 120 template<typename BlockType, size_t testBits = 12> VerifyShortMultiplication(bool bReportIndividualTestCases)121 int VerifyShortMultiplication(bool bReportIndividualTestCases) { 122 constexpr size_t nbits = 16; 123 constexpr size_t NR_INTEGERS = (size_t(1) << testBits); 124 125 using Integer = integer<nbits, BlockType>; 126 Integer ia, ib, iresult, iref; 127 128 int nrOfFailedTests = 0; 129 for (size_t i = 0; i < NR_INTEGERS; i++) { 130 ia.setbits(i); 131 short i16a = short(ia); 132 for (size_t j = 0; j < NR_INTEGERS; j++) { 133 ib.setbits(j); 134 short i16b = short(ib); 135 iref = i16a * i16b; 136 #if INTEGER_THROW_ARITHMETIC_EXCEPTION 137 try { 138 iresult = ia * ib; 139 } 140 catch (...) { 141 Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg); 142 if (iref > max_int || iref < min_int) { 143 // correctly caught the exception 144 continue; 145 } 146 else { 147 nrOfFailedTests++; 148 } 149 } 150 151 #else 152 iresult = ia * ib; 153 #endif 154 if (iresult != iref) { 155 nrOfFailedTests++; 156 if (bReportIndividualTestCases) ReportBinaryArithmeticError("FAIL", "*", ia, ib, iref, iresult); 157 } 158 else { 159 //if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "*", ia, ib, iref, iresult); 160 } 161 } 162 if (i % 1024 == 0) std::cout << '.'; 163 } 164 std::cout << std::endl; 165 166 return nrOfFailedTests; 167 } 168 // enumerate all division cases for an integer<16> configuration compared against native short 169 template<typename BlockType, size_t testBits = 10> VerifyShortDivision(bool bReportIndividualTestCases)170 int VerifyShortDivision(bool bReportIndividualTestCases) { 171 constexpr size_t nbits = 16; 172 constexpr size_t NR_INTEGERS = (size_t(1) << testBits); 173 174 using Integer = integer<nbits, BlockType>; 175 Integer ia, ib, iresult, iref; 176 177 int nrOfFailedTests = 0; 178 for (size_t i = 0; i < NR_INTEGERS; i++) { 179 ia.setbits(i); 180 short i16a = short(ia); 181 for (size_t j = 0; j < NR_INTEGERS; j++) { 182 ib.setbits(j); 183 short i16b = short(ib); 184 if (j > 0) iref = i16a / i16b; 185 #if INTEGER_THROW_ARITHMETIC_EXCEPTION 186 if (j == 0) { 187 try { 188 iresult = ia / ib; 189 } 190 catch (const integer_divide_by_zero& err) { 191 // correctly caught overflow 192 continue; 193 } 194 catch (...) { 195 nrOfFailedTests++; 196 } 197 } 198 Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg); 199 if (iref > max_int || iref < min_int) { 200 try { 201 iresult = ia / ib; 202 } 203 catch (const integer_overflow& err) { 204 // correctly caught overflow 205 continue; 206 } 207 catch (...) { 208 nrOfFailedTests++; 209 } 210 } 211 #else 212 if (j == 0) continue; 213 iresult = ia / ib; 214 #endif 215 216 if (iresult != iref) { 217 nrOfFailedTests++; 218 if (bReportIndividualTestCases) ReportBinaryArithmeticError("FAIL", "/", ia, ib, iref, iresult); 219 } 220 else { 221 //if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "/", ia, ib, iref, iresult); 222 } 223 } 224 if (i % 1024 == 0) std::cout << '.'; 225 } 226 std::cout << std::endl; 227 228 return nrOfFailedTests; 229 } 230 // enumerate all remainder cases for an integer<16> configuration compared against native short 231 template<typename BlockType, size_t testBits = 10> VerifyShortRemainder(bool bReportIndividualTestCases)232 int VerifyShortRemainder(bool bReportIndividualTestCases) { 233 constexpr size_t nbits = 16; 234 constexpr size_t NR_INTEGERS = (size_t(1) << testBits); 235 236 using Integer = integer<nbits, BlockType>; 237 Integer ia, ib, iresult, iref; 238 239 int nrOfFailedTests = 0; 240 for (size_t i = 0; i < NR_INTEGERS; i++) { 241 ia.setbits(i); 242 short i16a = short(ia); 243 for (size_t j = 0; j < NR_INTEGERS; j++) { 244 ib.setbits(j); 245 short i16b = short(ib); 246 if (j > 0) iref = i16a % i16b; 247 #if INTEGER_THROW_ARITHMETIC_EXCEPTION 248 try { 249 iresult = ia % ib; 250 } 251 catch (...) { 252 Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg); 253 if (iref > max_int || iref < min_int) { 254 // correctly caught the exception 255 continue; 256 } 257 else { 258 nrOfFailedTests++; 259 } 260 } 261 #else 262 iresult = ia % ib; 263 #endif 264 if (iresult != iref) { 265 nrOfFailedTests++; 266 if (bReportIndividualTestCases) ReportBinaryArithmeticError("FAIL", "%", ia, ib, iref, iresult); 267 } 268 else { 269 //if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "%", ia, ib, iref, iresult); 270 } 271 } 272 if (i % 1024 == 0) std::cout << '.'; 273 } 274 std::cout << std::endl; 275 276 return nrOfFailedTests; 277 } 278 279 // enumerate all addition cases for an integer<nbits, BlockType> configuration 280 template<size_t nbits, typename BlockType> VerifyAddition(bool bReportIndividualTestCases)281 int VerifyAddition(bool bReportIndividualTestCases) { 282 using Integer = integer<nbits, BlockType>; 283 constexpr size_t NR_INTEGERS = (size_t(1) << nbits); 284 285 Integer ia, ib, iresult, iref; 286 287 int nrOfFailedTests = 0; 288 for (size_t i = 0; i < NR_INTEGERS; i++) { 289 ia.setbits(i); 290 int64_t i64a = int64_t(ia); 291 for (size_t j = 0; j < NR_INTEGERS; j++) { 292 ib.setbits(j); 293 int64_t i64b = int64_t(ib); 294 iref = i64a + i64b; 295 #if INTEGER_THROW_ARITHMETIC_EXCEPTION 296 try { 297 iresult = ia + ib; 298 } 299 catch (...) { 300 Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg); 301 if (iref > max_int || iref < min_int) { 302 // correctly caught the exception 303 304 } 305 else { 306 nrOfFailedTests++; 307 } 308 } 309 310 #else 311 iresult = ia + ib; 312 #endif 313 if (iresult != iref) { 314 nrOfFailedTests++; 315 if (bReportIndividualTestCases) ReportBinaryArithmeticError("FAIL", "+", ia, ib, iref, iresult); 316 } 317 else { 318 //if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "+", ia, ib, iref, iresult); 319 } 320 if (nrOfFailedTests > 100) return nrOfFailedTests; 321 } 322 if (i % 1024 == 0) std::cout << '.'; 323 } 324 std::cout << std::endl; 325 return nrOfFailedTests; 326 } 327 // enumerate all subtraction cases for an integer<nbits, BlockType> configuration 328 template<size_t nbits, typename BlockType> VerifySubtraction(bool bReportIndividualTestCases)329 int VerifySubtraction(bool bReportIndividualTestCases) { 330 using Integer = integer<nbits, BlockType>; 331 constexpr size_t NR_INTEGERS = (size_t(1) << nbits); 332 333 Integer ia, ib, iresult, iref; 334 335 int nrOfFailedTests = 0; 336 for (size_t i = 0; i < NR_INTEGERS; i++) { 337 ia.setbits(i); 338 int64_t i64a = int64_t(ia); 339 for (size_t j = 0; j < NR_INTEGERS; j++) { 340 ib.setbits(j); 341 int64_t i64b = int64_t(ib); 342 iref = i64a - i64b; 343 #if INTEGER_THROW_ARITHMETIC_EXCEPTION 344 try { 345 iresult = ia - ib; 346 } 347 catch (...) { 348 Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg); 349 if (iref > max_int || iref < min_int) { 350 // correctly caught the exception 351 352 } 353 else { 354 nrOfFailedTests++; 355 } 356 } 357 358 #else 359 iresult = ia - ib; 360 #endif 361 if (iresult != iref) { 362 nrOfFailedTests++; 363 if (bReportIndividualTestCases) ReportBinaryArithmeticError("FAIL", "-", ia, ib, iref, iresult); 364 } 365 else { 366 //if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "-", ia, ib, iref, iresult); 367 } 368 if (nrOfFailedTests > 100) return nrOfFailedTests; 369 } 370 if (i % 1024 == 0) std::cout << '.'; 371 } 372 std::cout << std::endl; 373 return nrOfFailedTests; 374 } 375 376 // enumerate all multiplication cases for an integer<nbits, BlockType> configuration 377 template<size_t nbits, typename BlockType> VerifyMultiplication(bool bReportIndividualTestCases)378 int VerifyMultiplication(bool bReportIndividualTestCases) { 379 using Integer = integer<nbits, BlockType>; 380 constexpr size_t NR_INTEGERS = (size_t(1) << nbits); 381 382 Integer ia, ib, iresult, iref; 383 384 int nrOfFailedTests = 0; 385 for (size_t i = 0; i < NR_INTEGERS; i++) { 386 ia.setbits(i); 387 int64_t i64a = int64_t(ia); 388 for (size_t j = 0; j < NR_INTEGERS; j++) { 389 ib.setbits(j); 390 int64_t i64b = int64_t(ib); 391 iref = i64a * i64b; 392 #if INTEGER_THROW_ARITHMETIC_EXCEPTION 393 try { 394 iresult = ia * ib; 395 } 396 catch (...) { 397 Integer max_int(SpecificValue::maxpos), min_int(SpecificValue::maxneg); 398 if (iref > max_int || iref < min_int) { 399 // correctly caught the exception 400 401 } 402 else { 403 nrOfFailedTests++; 404 } 405 } 406 407 #else 408 iresult = ia * ib; 409 #endif 410 if (iresult != iref) { 411 nrOfFailedTests++; 412 if (bReportIndividualTestCases) ReportBinaryArithmeticError("FAIL", "*", ia, ib, iref, iresult); 413 } 414 else { 415 if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "*", ia, ib, iref, iresult); 416 } 417 if (nrOfFailedTests > 100) return nrOfFailedTests; 418 } 419 if (i % 1024 == 0) std::cout << '.'; 420 } 421 std::cout << std::endl; 422 return nrOfFailedTests; 423 } 424 425 // enumerate all division cases for an integer<nbits, BlockType> configuration 426 template<size_t nbits, typename BlockType> VerifyDivision(bool bReportIndividualTestCases)427 int VerifyDivision(bool bReportIndividualTestCases) { 428 using Integer = integer<nbits, BlockType>; 429 constexpr size_t NR_INTEGERS = (size_t(1) << nbits); 430 431 Integer ia, ib, iresult, iref; 432 433 int nrOfFailedTests = 0; 434 for (size_t i = 0; i < NR_INTEGERS; i++) { 435 ia.setbits(i); 436 int64_t i64a = int64_t(ia); 437 for (size_t j = 0; j < NR_INTEGERS; j++) { 438 ib.setbits(j); 439 int64_t i64b = int64_t(ib); 440 #if INTEGER_THROW_ARITHMETIC_EXCEPTION 441 try { 442 iresult = ia / ib; 443 } 444 catch (const integer_divide_by_zero& e) { 445 if (ib == integer<nbits, BlockType>(0)) { 446 // correctly caught the exception 447 continue; 448 } 449 else { 450 std::cerr << "unexpected : " << e.what() << std::endl; 451 nrOfFailedTests++; 452 } 453 } 454 catch (const integer_overflow& e) { 455 std::cerr << e.what() << std::endl; 456 // TODO: how do you validate the overflow? 457 } 458 catch (...) { 459 std::cerr << "unexpected exception" << std::endl; 460 nrOfFailedTests++; 461 } 462 #else 463 iresult = ia / ib; 464 #endif 465 if (j == 0) { 466 iref = 0; // or maxneg? 467 } 468 else { 469 iref = i64a / i64b; 470 } 471 if (iresult != iref) { 472 nrOfFailedTests++; 473 if (bReportIndividualTestCases) ReportBinaryArithmeticError("FAIL", "/", ia, ib, iref, iresult); 474 } 475 else { 476 //if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "/", ia, ib, iref, iresult); 477 } 478 if (nrOfFailedTests > 100) return nrOfFailedTests; 479 } 480 if (i % 1024 == 0) std::cout << '.'; 481 } 482 std::cout << std::endl; 483 return nrOfFailedTests; 484 } 485 486 // enumerate all remainder cases for an integer<nbits, BlockType> configuration 487 template<size_t nbits, typename BlockType> VerifyRemainder(bool bReportIndividualTestCases)488 int VerifyRemainder(bool bReportIndividualTestCases) { 489 using Integer = integer<nbits, BlockType>; 490 constexpr size_t NR_INTEGERS = (size_t(1) << nbits); 491 492 Integer ia, ib, iresult, iref; 493 494 int nrOfFailedTests = 0; 495 for (size_t i = 0; i < NR_INTEGERS; i++) { 496 ia.setbits(i); 497 int64_t i64a = int64_t(ia); 498 for (size_t j = 0; j < NR_INTEGERS; j++) { 499 ib.setbits(j); 500 int64_t i64b = int64_t(ib); 501 #if INTEGER_THROW_ARITHMETIC_EXCEPTION 502 try { 503 iresult = ia % ib; 504 } 505 catch (...) { 506 if (ib == integer<nbits, BlockType>(0)) { 507 // correctly caught the exception 508 continue; 509 } 510 else { 511 nrOfFailedTests++; 512 } 513 } 514 515 #else 516 iresult = ia % ib; 517 #endif 518 iref = i64a % i64b; 519 if (iresult != iref) { 520 nrOfFailedTests++; 521 if (bReportIndividualTestCases) ReportBinaryArithmeticError("FAIL", "%", ia, ib, iref, iresult); 522 } 523 else { 524 //if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "%", ia, ib, iref, iresult); 525 } 526 if (nrOfFailedTests > 100) return nrOfFailedTests; 527 } 528 if (i % 1024 == 0) std::cout << '.'; 529 } 530 std::cout << std::endl; 531 return nrOfFailedTests; 532 } 533 534 } // namespace sw::universal 535