1 // urmul.cpp: functional tests for unrounded block binary multiplication
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 <universal/utility/directives.hpp>
7 #include <universal/utility/long_double.hpp>
8 #include <iostream>
9 #include <iomanip>
10
11 #include <universal/internal/blockbinary/blockbinary.hpp>
12 #include <universal/verification/test_status.hpp> // ReportTestResult
13 #include <universal/verification/blockbinary_test_status.hpp>
14
15 // enumerate all multiplication cases for an blockbinary<nbits,BlockType> configuration
16 template<size_t nbits, typename BlockType = uint8_t>
VerifyUnroundedMultiplication(bool bReportIndividualTestCases)17 int VerifyUnroundedMultiplication(bool bReportIndividualTestCases) {
18 constexpr size_t NR_VALUES = (size_t(1) << nbits);
19 using namespace sw::universal;
20
21 std::cout << "unrounded multiplication for blockbinary<" << nbits << ',' << typeid(BlockType).name() << ">\n";
22
23 bool bReportOverflowCondition = false;
24 int nrOfFailedTests = 0;
25 int nrOfOverflows = 0; // ref > maxpos
26 int nrOfUnderflows = 0; // ref < maxneg
27 blockbinary<nbits, BlockType> a, b;
28 blockbinary<2*nbits, BlockType> signext_a, signext_b, signext_result, result_reference;
29 int64_t aref, bref, cref;
30 for (size_t i = 0; i < NR_VALUES; i++) {
31 a.setbits(i);
32 signext_a = a;
33 aref = int64_t(signext_a.to_long_long()); // cast to long long is reasonable constraint for exhaustive test
34 for (size_t j = 0; j < NR_VALUES; j++) {
35 b.setbits(j);
36 signext_b = b;
37 bref = int64_t(signext_b.to_long_long()); // cast to long long is reasonable constraint for exhaustive test
38 signext_result = signext_a * signext_b;
39 cref = aref * bref;
40
41 if (bReportOverflowCondition) std::cout << std::setw(5) << aref << " * " << std::setw(5) << bref << " = " << std::setw(5) << cref << " : ";
42 if (cref < -(1 << (nbits - 1))) {
43 if (bReportOverflowCondition) std::cout << "underflow: " << std::setw(5) << cref << " < " << std::setw(5) << -(1 << (nbits - 1)) << "(maxneg) assigned value = " << std::setw(5) << signext_result.to_long_long() << " " << std::setw(5) << to_hex(signext_result) << " vs " << to_binary(cref, 12) << '\n';
44 ++nrOfUnderflows;
45 }
46 else if (cref > ((1 << (nbits - 1)) - 1)) {
47 if (bReportOverflowCondition) std::cout << "overflow: " << std::setw(5) << cref << " > " << std::setw(5) << (1 << (nbits - 1)) - 1 << "(maxpos) assigned value = " << std::setw(5) << signext_result.to_long_long() << " " << std::setw(5) << to_hex(signext_result) << " vs " << to_binary(cref, 12) << '\n';
48 ++nrOfOverflows;
49 }
50 else {
51 if (bReportOverflowCondition) std::cout << '\n';
52 }
53
54 result_reference.setbits(static_cast<uint64_t>(cref)); // in 2*nbits representation
55 if (signext_result != result_reference) {
56 nrOfFailedTests++;
57 if (bReportIndividualTestCases) ReportBinaryArithmeticError("FAIL", "*", signext_a, signext_b, signext_result, cref);
58 }
59 else {
60 // if (bReportIndividualTestCases) ReportBinaryArithmeticSuccess("PASS", "*", signext_a, signext_b, signext_result, cref);
61 }
62 if (nrOfFailedTests > 100) return nrOfFailedTests;
63 }
64 // if (i % 1024 == 0) std::cout << '.';
65 }
66 std::cout << "Total State Space: " << std::setw(10) << NR_VALUES * NR_VALUES
67 << " Overflows: " << std::setw(10) << nrOfOverflows << " Underflows " << std::setw(10) << nrOfUnderflows << '\n';
68 return nrOfFailedTests;
69 }
70
71 // generate specific test case that you can trace with the trace conditions in fixpnt.h
72 // for most bugs they are traceable with _trace_conversion and _trace_add
73 template<size_t nbits, typename StorageBlockType = uint8_t>
GenerateTestCase(int64_t lhs,int64_t rhs)74 void GenerateTestCase(int64_t lhs, int64_t rhs) {
75 using namespace sw::universal;
76 blockbinary<nbits, StorageBlockType> a, b, result, reference;
77
78 a.setbits(uint64_t(lhs));
79 b.setbits(uint64_t(rhs));
80 long long _a, _b, _c;
81 _a = (long long)a;
82 _b = (long long)b;
83 result = a * b;
84 _c = (long long)result;
85
86
87 std::streamsize oldPrecision = std::cout.precision();
88 std::cout << std::setprecision(nbits - 2);
89 std::cout << std::setw(nbits) << _a << " * " << std::setw(nbits) << _b << " = " << std::setw(nbits) << _a * _b << std::endl;
90 std::cout << to_binary(a) << " * " << to_binary(b) << " = " << to_binary(result) << " (reference: " << _a * _b << ") " << std::endl;
91 // std::cout << to_hex(a) << " * " << to_hex(b) << " = " << to_hex(result) << " (reference: " << std::hex << ref << ") ";
92 reference.setbits(_a * _b);
93 std::cout << (result == reference ? "PASS" : "FAIL") << std::endl << std::endl;
94 std::cout << std::dec << std::setprecision(oldPrecision);
95 }
96
97 // conditional compile flags
98 #define MANUAL_TESTING 0
99 #define STRESS_TESTING 0
100
main()101 int main()
102 try {
103 using namespace sw::universal;
104
105 std::string test_suite = "unrounded blockbinary multiplication";
106 std::string test_tag = "unrounded multiplication";
107 std::cout << test_suite << '\n';
108 bool bReportIndividualTestCases = false;
109 int nrOfFailedTestCases = 0;
110
111 #if MANUAL_TESTING
112
113 blockbinary<4> a, b;
114 a = -8;
115 b = -8;
116 blockbinary<8> c = urmul(a, b);
117 cout << (long long)a << " * " << (long long)b << " = " << (long long)c << " : " << to_binary(c) << " <--- demonstration that 2*nbits is sufficient to represent all results\n";
118
119 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<4, uint8_t>(true), "blockbinary<4,uint8>", test_tag);
120 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<8, uint8_t>(true), "blockbinary<8,uint8>", test_tag);
121 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<8, uint16_t>(true), "blockbinary<8,uint16>", test_tag);
122
123 nrOfFailedTestCases = 0;
124
125 #if STRESS_TESTING
126
127
128 #endif
129
130 #else
131
132 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication< 4, uint8_t >(bReportIndividualTestCases), "blockbinary< 8,uint8 >", test_tag);
133 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication< 4, uint16_t>(bReportIndividualTestCases), "blockbinary< 8,uint16>", test_tag);
134 // nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication< 4, uint32_t>(bReportIndividualTestCases), "blockbinary< 8,uint32>", test_tag);
135
136 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication< 8, uint8_t >(bReportIndividualTestCases), "blockbinary< 8,uint8 >", test_tag);
137 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication< 8, uint16_t>(bReportIndividualTestCases), "blockbinary< 8,uint16>", test_tag);
138 // nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication< 8, uint32_t>(bReportIndividualTestCases), "blockbinary< 8,uint32>", test_tag);
139
140 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication< 9, uint8_t >(bReportIndividualTestCases), "blockbinary< 9,uint8 >", test_tag);
141 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication< 9, uint16_t>(bReportIndividualTestCases), "blockbinary< 9,uint16>", test_tag);
142 // nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication< 9, uint32_t>(bReportIndividualTestCases), "blockbinary< 9,uint32>", test_tag);
143
144 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<10, uint8_t >(bReportIndividualTestCases), "blockbinary<10,uint8 >", test_tag);
145 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<10, uint16_t>(bReportIndividualTestCases), "blockbinary<10,uint16>", test_tag);
146 // nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<10, uint32_t>(bReportIndividualTestCases), "blockbinary<10,uint32>", test_tag);
147
148
149 #if STRESS_TESTING
150
151 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<11, uint8_t>(bReportIndividualTestCases), "blockbinary<11,uint8>", test_tag);
152 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<11, uint16_t>(bReportIndividualTestCases), "blockbinary<11,uint16>", test_tag);
153 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<11, uint32_t>(bReportIndividualTestCases), "blockbinary<11,uint32>", test_tag);
154
155 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<12, uint8_t>(bReportIndividualTestCases), "blockbinary<12,uint8>", test_tag);
156 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<12, uint16_t>(bReportIndividualTestCases), "blockbinary<12,uint16>", test_tag);
157 nrOfFailedTestCases += ReportTestResult(VerifyUnroundedMultiplication<12, uint32_t>(bReportIndividualTestCases), "blockbinary<12,uint32>", test_tag);
158
159 #endif // STRESS_TESTING
160
161 #endif // MANUAL_TESTING
162
163 return (nrOfFailedTestCases > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
164 }
165 catch (char const* msg) {
166 std::cerr << msg << std::endl;
167 return EXIT_FAILURE;
168 }
169 catch (const std::runtime_error& err) {
170 std::cerr << "Uncaught runtime exception: " << err.what() << std::endl;
171 return EXIT_FAILURE;
172 }
173 catch (...) {
174 std::cerr << "Caught unknown exception" << std::endl;
175 return EXIT_FAILURE;
176 }
177