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