xref: /aosp_15_r20/external/XNNPACK/test/requantization-tester.h (revision 4bdc94577ba0e567308109d787f7fec7b531ce36)
1*4bdc9457SAndroid Build Coastguard Worker // Copyright (c) Facebook, Inc. and its affiliates.
2*4bdc9457SAndroid Build Coastguard Worker // All rights reserved.
3*4bdc9457SAndroid Build Coastguard Worker //
4*4bdc9457SAndroid Build Coastguard Worker // Copyright 2019 Google LLC
5*4bdc9457SAndroid Build Coastguard Worker //
6*4bdc9457SAndroid Build Coastguard Worker // This source code is licensed under the BSD-style license found in the
7*4bdc9457SAndroid Build Coastguard Worker // LICENSE file in the root directory of this source tree.
8*4bdc9457SAndroid Build Coastguard Worker 
9*4bdc9457SAndroid Build Coastguard Worker #pragma once
10*4bdc9457SAndroid Build Coastguard Worker 
11*4bdc9457SAndroid Build Coastguard Worker #include <gtest/gtest.h>
12*4bdc9457SAndroid Build Coastguard Worker 
13*4bdc9457SAndroid Build Coastguard Worker #include <algorithm>
14*4bdc9457SAndroid Build Coastguard Worker #include <cfloat>
15*4bdc9457SAndroid Build Coastguard Worker #include <cmath>
16*4bdc9457SAndroid Build Coastguard Worker #include <cstddef>
17*4bdc9457SAndroid Build Coastguard Worker #include <cstdlib>
18*4bdc9457SAndroid Build Coastguard Worker #include <functional>
19*4bdc9457SAndroid Build Coastguard Worker #include <limits>
20*4bdc9457SAndroid Build Coastguard Worker #include <random>
21*4bdc9457SAndroid Build Coastguard Worker #include <vector>
22*4bdc9457SAndroid Build Coastguard Worker 
23*4bdc9457SAndroid Build Coastguard Worker #include <xnnpack/requantization-stubs.h>
24*4bdc9457SAndroid Build Coastguard Worker #include <xnnpack/requantization.h>
25*4bdc9457SAndroid Build Coastguard Worker 
26*4bdc9457SAndroid Build Coastguard Worker 
27*4bdc9457SAndroid Build Coastguard Worker class RequantizationTester {
28*4bdc9457SAndroid Build Coastguard Worker  public:
s(uint32_t s)29*4bdc9457SAndroid Build Coastguard Worker   inline RequantizationTester& s(uint32_t s) {
30*4bdc9457SAndroid Build Coastguard Worker     this->s_ = s;
31*4bdc9457SAndroid Build Coastguard Worker     return *this;
32*4bdc9457SAndroid Build Coastguard Worker   }
33*4bdc9457SAndroid Build Coastguard Worker 
s()34*4bdc9457SAndroid Build Coastguard Worker   inline uint32_t s() const {
35*4bdc9457SAndroid Build Coastguard Worker     return this->s_;
36*4bdc9457SAndroid Build Coastguard Worker   }
37*4bdc9457SAndroid Build Coastguard Worker 
scale()38*4bdc9457SAndroid Build Coastguard Worker   inline float scale() const {
39*4bdc9457SAndroid Build Coastguard Worker     return ldexpf(1.0f, -s());
40*4bdc9457SAndroid Build Coastguard Worker   }
41*4bdc9457SAndroid Build Coastguard Worker 
zero_point(int32_t zero_point)42*4bdc9457SAndroid Build Coastguard Worker   inline RequantizationTester& zero_point(int32_t zero_point) {
43*4bdc9457SAndroid Build Coastguard Worker     this->zero_point_ = zero_point;
44*4bdc9457SAndroid Build Coastguard Worker     return *this;
45*4bdc9457SAndroid Build Coastguard Worker   }
46*4bdc9457SAndroid Build Coastguard Worker 
zero_point()47*4bdc9457SAndroid Build Coastguard Worker   inline int32_t zero_point() const {
48*4bdc9457SAndroid Build Coastguard Worker     return this->zero_point_;
49*4bdc9457SAndroid Build Coastguard Worker   }
50*4bdc9457SAndroid Build Coastguard Worker 
qmin(int16_t qmin)51*4bdc9457SAndroid Build Coastguard Worker   inline RequantizationTester& qmin(int16_t qmin) {
52*4bdc9457SAndroid Build Coastguard Worker     this->qmin_ = qmin;
53*4bdc9457SAndroid Build Coastguard Worker     return *this;
54*4bdc9457SAndroid Build Coastguard Worker   }
55*4bdc9457SAndroid Build Coastguard Worker 
qmin()56*4bdc9457SAndroid Build Coastguard Worker   inline int16_t qmin() const {
57*4bdc9457SAndroid Build Coastguard Worker     return this->qmin_;
58*4bdc9457SAndroid Build Coastguard Worker   }
59*4bdc9457SAndroid Build Coastguard Worker 
qmax(int16_t qmax)60*4bdc9457SAndroid Build Coastguard Worker   inline RequantizationTester& qmax(int16_t qmax) {
61*4bdc9457SAndroid Build Coastguard Worker     this->qmax_ = qmax;
62*4bdc9457SAndroid Build Coastguard Worker     return *this;
63*4bdc9457SAndroid Build Coastguard Worker   }
64*4bdc9457SAndroid Build Coastguard Worker 
qmax()65*4bdc9457SAndroid Build Coastguard Worker   inline int16_t qmax() const {
66*4bdc9457SAndroid Build Coastguard Worker     return this->qmax_;
67*4bdc9457SAndroid Build Coastguard Worker   }
68*4bdc9457SAndroid Build Coastguard Worker 
iterations(size_t iterations)69*4bdc9457SAndroid Build Coastguard Worker   inline RequantizationTester& iterations(size_t iterations) {
70*4bdc9457SAndroid Build Coastguard Worker     this->iterations_ = iterations;
71*4bdc9457SAndroid Build Coastguard Worker     return *this;
72*4bdc9457SAndroid Build Coastguard Worker   }
73*4bdc9457SAndroid Build Coastguard Worker 
iterations()74*4bdc9457SAndroid Build Coastguard Worker   inline size_t iterations() const {
75*4bdc9457SAndroid Build Coastguard Worker     return this->iterations_;
76*4bdc9457SAndroid Build Coastguard Worker   }
77*4bdc9457SAndroid Build Coastguard Worker 
78*4bdc9457SAndroid Build Coastguard Worker   /*
79*4bdc9457SAndroid Build Coastguard Worker    * Test that requantization of numbers ((i - zero point) * 2**s) with
80*4bdc9457SAndroid Build Coastguard Worker    * - scale = exp2(-s)
81*4bdc9457SAndroid Build Coastguard Worker    * - zero point in [0, 255]
82*4bdc9457SAndroid Build Coastguard Worker    * - no output clamping
83*4bdc9457SAndroid Build Coastguard Worker    * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow.
84*4bdc9457SAndroid Build Coastguard Worker    */
TestExactDivideByPO2(xnn_qu8_requantization_function requantize)85*4bdc9457SAndroid Build Coastguard Worker   void TestExactDivideByPO2(xnn_qu8_requantization_function requantize) const {
86*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min());
87*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max());
88*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min());
89*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max());
90*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min());
91*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max());
92*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
93*4bdc9457SAndroid Build Coastguard Worker 
94*4bdc9457SAndroid Build Coastguard Worker     /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */
95*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(s(), 1);
96*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(s(), 32);
97*4bdc9457SAndroid Build Coastguard Worker 
98*4bdc9457SAndroid Build Coastguard Worker     std::vector<int32_t> inputs(256);
99*4bdc9457SAndroid Build Coastguard Worker     std::vector<uint8_t> outputs(inputs.size());
100*4bdc9457SAndroid Build Coastguard Worker     const int32_t max_i = (uint32_t(std::numeric_limits<int32_t>::max()) >> s()) + zero_point();
101*4bdc9457SAndroid Build Coastguard Worker     const int32_t min_i = -(-uint32_t(std::numeric_limits<int32_t>::min()) >> s()) + zero_point();
102*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) {
103*4bdc9457SAndroid Build Coastguard Worker       const int32_t clamped_i = std::max(min_i, std::min(max_i, i));
104*4bdc9457SAndroid Build Coastguard Worker       inputs[i] = int32_t(uint32_t(clamped_i - zero_point()) << s());
105*4bdc9457SAndroid Build Coastguard Worker     }
106*4bdc9457SAndroid Build Coastguard Worker     requantize(inputs.size(), inputs.data(),
107*4bdc9457SAndroid Build Coastguard Worker         scale(), zero_point(), qmin(), qmax(),
108*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
109*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) {
110*4bdc9457SAndroid Build Coastguard Worker       const int32_t clamped_i = std::max(min_i, std::min(max_i, i));
111*4bdc9457SAndroid Build Coastguard Worker       ASSERT_EQ(uint32_t(clamped_i), uint32_t(outputs[i]))
112*4bdc9457SAndroid Build Coastguard Worker         << "i = " << i << ", clamped i = " << clamped_i << ", input = " << inputs[i]
113*4bdc9457SAndroid Build Coastguard Worker         << ", min i = " << min_i << ", max i = " << max_i
114*4bdc9457SAndroid Build Coastguard Worker         << ", s = " << s() << ", zero point = " << zero_point();
115*4bdc9457SAndroid Build Coastguard Worker     }
116*4bdc9457SAndroid Build Coastguard Worker   }
117*4bdc9457SAndroid Build Coastguard Worker 
118*4bdc9457SAndroid Build Coastguard Worker   /*
119*4bdc9457SAndroid Build Coastguard Worker    * Test that requantization of numbers ((i - zero point) * 2**s) with
120*4bdc9457SAndroid Build Coastguard Worker    * - scale = exp2(-s)
121*4bdc9457SAndroid Build Coastguard Worker    * - zero point in [-128, 127]
122*4bdc9457SAndroid Build Coastguard Worker    * - no output clamping
123*4bdc9457SAndroid Build Coastguard Worker    * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow.
124*4bdc9457SAndroid Build Coastguard Worker    */
TestExactDivideByPO2(xnn_qs8_requantization_function requantize)125*4bdc9457SAndroid Build Coastguard Worker   void TestExactDivideByPO2(xnn_qs8_requantization_function requantize) const {
126*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min());
127*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max());
128*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min());
129*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max());
130*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min());
131*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max());
132*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
133*4bdc9457SAndroid Build Coastguard Worker 
134*4bdc9457SAndroid Build Coastguard Worker     /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */
135*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(s(), 1);
136*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(s(), 32);
137*4bdc9457SAndroid Build Coastguard Worker 
138*4bdc9457SAndroid Build Coastguard Worker     std::vector<int32_t> inputs(256);
139*4bdc9457SAndroid Build Coastguard Worker     std::vector<int8_t> outputs(inputs.size());
140*4bdc9457SAndroid Build Coastguard Worker     const int32_t max_i = (uint32_t(std::numeric_limits<int32_t>::max()) >> s()) + zero_point();
141*4bdc9457SAndroid Build Coastguard Worker     const int32_t min_i = -(-uint32_t(std::numeric_limits<int32_t>::min()) >> s()) + zero_point();
142*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) {
143*4bdc9457SAndroid Build Coastguard Worker       const int32_t clamped_i = std::max(min_i, std::min(max_i, i));
144*4bdc9457SAndroid Build Coastguard Worker       inputs[i - std::numeric_limits<int8_t>::min()] = int32_t(uint32_t(clamped_i - zero_point()) << s());
145*4bdc9457SAndroid Build Coastguard Worker     }
146*4bdc9457SAndroid Build Coastguard Worker     requantize(inputs.size(), inputs.data(),
147*4bdc9457SAndroid Build Coastguard Worker         scale(), zero_point(), qmin(), qmax(),
148*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
149*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) {
150*4bdc9457SAndroid Build Coastguard Worker       const int32_t clamped_i = std::max(min_i, std::min(max_i, i));
151*4bdc9457SAndroid Build Coastguard Worker       ASSERT_EQ(clamped_i, int32_t(outputs[i - std::numeric_limits<int8_t>::min()]))
152*4bdc9457SAndroid Build Coastguard Worker         << "i = " << i << ", clamped i = " << clamped_i
153*4bdc9457SAndroid Build Coastguard Worker         << ", input = " << inputs[i - std::numeric_limits<int8_t>::min()]
154*4bdc9457SAndroid Build Coastguard Worker         << ", min i = " << min_i << ", max i = " << max_i
155*4bdc9457SAndroid Build Coastguard Worker         << ", s = " << s() << ", zero point = " << zero_point();
156*4bdc9457SAndroid Build Coastguard Worker     }
157*4bdc9457SAndroid Build Coastguard Worker   }
158*4bdc9457SAndroid Build Coastguard Worker 
159*4bdc9457SAndroid Build Coastguard Worker   /*
160*4bdc9457SAndroid Build Coastguard Worker    * Test that requantization of numbers ((i - zero point) * 2**s - 2**(s-1) + 1) with
161*4bdc9457SAndroid Build Coastguard Worker    * - scale = exp2(-s)
162*4bdc9457SAndroid Build Coastguard Worker    * - zero point in [1, 255]
163*4bdc9457SAndroid Build Coastguard Worker    * - no output clamping
164*4bdc9457SAndroid Build Coastguard Worker    * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow.
165*4bdc9457SAndroid Build Coastguard Worker    */
TestDivideByPO2WithRoundingUp(xnn_qu8_requantization_function requantize)166*4bdc9457SAndroid Build Coastguard Worker   void TestDivideByPO2WithRoundingUp(xnn_qu8_requantization_function requantize) {
167*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min());
168*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max());
169*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min());
170*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max());
171*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min());
172*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max());
173*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
174*4bdc9457SAndroid Build Coastguard Worker 
175*4bdc9457SAndroid Build Coastguard Worker     /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */
176*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(s(), 1);
177*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(s(), 32);
178*4bdc9457SAndroid Build Coastguard Worker 
179*4bdc9457SAndroid Build Coastguard Worker     std::vector<int32_t> inputs(256);
180*4bdc9457SAndroid Build Coastguard Worker     std::vector<uint8_t> outputs(inputs.size());
181*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) {
182*4bdc9457SAndroid Build Coastguard Worker       const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) -
183*4bdc9457SAndroid Build Coastguard Worker         (INT64_C(1) << (s() - 1)) + 1;
184*4bdc9457SAndroid Build Coastguard Worker       inputs[i] = int32_t(input);
185*4bdc9457SAndroid Build Coastguard Worker     }
186*4bdc9457SAndroid Build Coastguard Worker     requantize(inputs.size(), inputs.data(),
187*4bdc9457SAndroid Build Coastguard Worker         scale(), zero_point(), qmin(), qmax(),
188*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
189*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) {
190*4bdc9457SAndroid Build Coastguard Worker       const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) -
191*4bdc9457SAndroid Build Coastguard Worker         (INT64_C(1) << (s() - 1)) + 1;
192*4bdc9457SAndroid Build Coastguard Worker       if (int32_t(input) == input) {
193*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(i, int32_t(outputs[i]))
194*4bdc9457SAndroid Build Coastguard Worker           << "i = " << i << ", input = " << input
195*4bdc9457SAndroid Build Coastguard Worker           << ", s = " << s() << ", zero point = " << zero_point();
196*4bdc9457SAndroid Build Coastguard Worker       }
197*4bdc9457SAndroid Build Coastguard Worker     }
198*4bdc9457SAndroid Build Coastguard Worker   }
199*4bdc9457SAndroid Build Coastguard Worker 
200*4bdc9457SAndroid Build Coastguard Worker   /*
201*4bdc9457SAndroid Build Coastguard Worker    * Test that requantization of numbers ((i - zero point) * 2**s - 2**(s-1) + 1) with
202*4bdc9457SAndroid Build Coastguard Worker    * - scale = exp2(-s)
203*4bdc9457SAndroid Build Coastguard Worker    * - zero point in [-128, 127]
204*4bdc9457SAndroid Build Coastguard Worker    * - no output clamping
205*4bdc9457SAndroid Build Coastguard Worker    * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow.
206*4bdc9457SAndroid Build Coastguard Worker    */
TestDivideByPO2WithRoundingUp(xnn_qs8_requantization_function requantize)207*4bdc9457SAndroid Build Coastguard Worker   void TestDivideByPO2WithRoundingUp(xnn_qs8_requantization_function requantize) {
208*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min());
209*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max());
210*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min());
211*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max());
212*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min());
213*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max());
214*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
215*4bdc9457SAndroid Build Coastguard Worker 
216*4bdc9457SAndroid Build Coastguard Worker     /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */
217*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(s(), 1);
218*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(s(), 32);
219*4bdc9457SAndroid Build Coastguard Worker 
220*4bdc9457SAndroid Build Coastguard Worker     std::vector<int32_t> inputs(256);
221*4bdc9457SAndroid Build Coastguard Worker     std::vector<int8_t> outputs(inputs.size());
222*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) {
223*4bdc9457SAndroid Build Coastguard Worker       const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) -
224*4bdc9457SAndroid Build Coastguard Worker         (INT64_C(1) << (s() - 1)) + 1;
225*4bdc9457SAndroid Build Coastguard Worker       inputs[i - std::numeric_limits<int8_t>::min()] = int32_t(input);
226*4bdc9457SAndroid Build Coastguard Worker     }
227*4bdc9457SAndroid Build Coastguard Worker     requantize(inputs.size(), inputs.data(),
228*4bdc9457SAndroid Build Coastguard Worker         scale(), zero_point(), qmin(), qmax(),
229*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
230*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) {
231*4bdc9457SAndroid Build Coastguard Worker       const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) -
232*4bdc9457SAndroid Build Coastguard Worker         (INT64_C(1) << (s() - 1)) + 1;
233*4bdc9457SAndroid Build Coastguard Worker       if (int32_t(input) == input) {
234*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(i, int32_t(outputs[i - std::numeric_limits<int8_t>::min()]))
235*4bdc9457SAndroid Build Coastguard Worker           << "i = " << i << ", input = " << input
236*4bdc9457SAndroid Build Coastguard Worker           << ", s = " << s() << ", zero point = " << zero_point();
237*4bdc9457SAndroid Build Coastguard Worker       }
238*4bdc9457SAndroid Build Coastguard Worker     }
239*4bdc9457SAndroid Build Coastguard Worker   }
240*4bdc9457SAndroid Build Coastguard Worker 
241*4bdc9457SAndroid Build Coastguard Worker   /*
242*4bdc9457SAndroid Build Coastguard Worker    * Test that requantization of numbers ((i - zero point) * 2**s + 2**(s-1) - 1) with
243*4bdc9457SAndroid Build Coastguard Worker    * - scale = exp2(-s)
244*4bdc9457SAndroid Build Coastguard Worker    * - zero point in [1, 255]
245*4bdc9457SAndroid Build Coastguard Worker    * - no output clamping
246*4bdc9457SAndroid Build Coastguard Worker    * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow.
247*4bdc9457SAndroid Build Coastguard Worker    */
TestDivideByPO2WithRoundingDown(xnn_qu8_requantization_function requantize)248*4bdc9457SAndroid Build Coastguard Worker   void TestDivideByPO2WithRoundingDown(xnn_qu8_requantization_function requantize) {
249*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min());
250*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max());
251*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min());
252*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max());
253*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min());
254*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max());
255*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
256*4bdc9457SAndroid Build Coastguard Worker 
257*4bdc9457SAndroid Build Coastguard Worker     /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */
258*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(s(), 1);
259*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(s(), 32);
260*4bdc9457SAndroid Build Coastguard Worker 
261*4bdc9457SAndroid Build Coastguard Worker     std::vector<int32_t> inputs(256);
262*4bdc9457SAndroid Build Coastguard Worker     std::vector<uint8_t> outputs(inputs.size());
263*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) {
264*4bdc9457SAndroid Build Coastguard Worker       const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) +
265*4bdc9457SAndroid Build Coastguard Worker         (INT64_C(1) << (s() - 1)) - 1;
266*4bdc9457SAndroid Build Coastguard Worker       inputs[i] = int32_t(input);
267*4bdc9457SAndroid Build Coastguard Worker     }
268*4bdc9457SAndroid Build Coastguard Worker     requantize(inputs.size(), inputs.data(),
269*4bdc9457SAndroid Build Coastguard Worker         scale(), zero_point(), qmin(), qmax(),
270*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
271*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) {
272*4bdc9457SAndroid Build Coastguard Worker       const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) +
273*4bdc9457SAndroid Build Coastguard Worker         (INT64_C(1) << (s() - 1)) - 1;
274*4bdc9457SAndroid Build Coastguard Worker       if (int32_t(input) == input) {
275*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(i, int32_t(outputs[i]))
276*4bdc9457SAndroid Build Coastguard Worker           << "i = " << i << ", input = " << input
277*4bdc9457SAndroid Build Coastguard Worker           << ", s = " << s() << ", zero point = " << zero_point();
278*4bdc9457SAndroid Build Coastguard Worker       }
279*4bdc9457SAndroid Build Coastguard Worker     }
280*4bdc9457SAndroid Build Coastguard Worker   }
281*4bdc9457SAndroid Build Coastguard Worker 
282*4bdc9457SAndroid Build Coastguard Worker   /*
283*4bdc9457SAndroid Build Coastguard Worker    * Test that requantization of numbers ((i - zero point) * 2**s + 2**(s-1) - 1) with
284*4bdc9457SAndroid Build Coastguard Worker    * - scale = exp2(-s)
285*4bdc9457SAndroid Build Coastguard Worker    * - zero point in [-128, 127]
286*4bdc9457SAndroid Build Coastguard Worker    * - no output clamping
287*4bdc9457SAndroid Build Coastguard Worker    * produces exactly i, provided that ((i - zero point) * 2**s) does not overflow.
288*4bdc9457SAndroid Build Coastguard Worker    */
TestDivideByPO2WithRoundingDown(xnn_qs8_requantization_function requantize)289*4bdc9457SAndroid Build Coastguard Worker   void TestDivideByPO2WithRoundingDown(xnn_qs8_requantization_function requantize) {
290*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min());
291*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max());
292*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min());
293*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max());
294*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min());
295*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max());
296*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
297*4bdc9457SAndroid Build Coastguard Worker 
298*4bdc9457SAndroid Build Coastguard Worker     /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */
299*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(s(), 1);
300*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(s(), 32);
301*4bdc9457SAndroid Build Coastguard Worker 
302*4bdc9457SAndroid Build Coastguard Worker     std::vector<int32_t> inputs(256);
303*4bdc9457SAndroid Build Coastguard Worker     std::vector<int8_t> outputs(inputs.size());
304*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) {
305*4bdc9457SAndroid Build Coastguard Worker       const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) +
306*4bdc9457SAndroid Build Coastguard Worker         (INT64_C(1) << (s() - 1)) - 1;
307*4bdc9457SAndroid Build Coastguard Worker       inputs[i - std::numeric_limits<int8_t>::min()] = int32_t(input);
308*4bdc9457SAndroid Build Coastguard Worker     }
309*4bdc9457SAndroid Build Coastguard Worker     requantize(inputs.size(), inputs.data(),
310*4bdc9457SAndroid Build Coastguard Worker         scale(), zero_point(), qmin(), qmax(),
311*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
312*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) {
313*4bdc9457SAndroid Build Coastguard Worker       const int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s()) +
314*4bdc9457SAndroid Build Coastguard Worker         (INT64_C(1) << (s() - 1)) - 1;
315*4bdc9457SAndroid Build Coastguard Worker       if (int32_t(input) == input) {
316*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(i, int32_t(outputs[i - std::numeric_limits<int8_t>::min()]))
317*4bdc9457SAndroid Build Coastguard Worker           << "i = " << i << ", input = " << input
318*4bdc9457SAndroid Build Coastguard Worker           << ", s = " << s() << ", zero point = " << zero_point();
319*4bdc9457SAndroid Build Coastguard Worker       }
320*4bdc9457SAndroid Build Coastguard Worker     }
321*4bdc9457SAndroid Build Coastguard Worker   }
322*4bdc9457SAndroid Build Coastguard Worker 
TestDivideByPO2WithRoundingTiesAway(xnn_qu8_requantization_function requantize)323*4bdc9457SAndroid Build Coastguard Worker   void TestDivideByPO2WithRoundingTiesAway(xnn_qu8_requantization_function requantize) {
324*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min());
325*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max());
326*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min());
327*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max());
328*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min());
329*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max());
330*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
331*4bdc9457SAndroid Build Coastguard Worker 
332*4bdc9457SAndroid Build Coastguard Worker     /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */
333*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(s(), 1);
334*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(s(), 32);
335*4bdc9457SAndroid Build Coastguard Worker 
336*4bdc9457SAndroid Build Coastguard Worker     std::vector<int32_t> inputs(256);
337*4bdc9457SAndroid Build Coastguard Worker     std::vector<uint8_t> outputs(inputs.size());
338*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) {
339*4bdc9457SAndroid Build Coastguard Worker       int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s());
340*4bdc9457SAndroid Build Coastguard Worker       if (input > 0) {
341*4bdc9457SAndroid Build Coastguard Worker         input -= INT64_C(1) << (s() - 1);
342*4bdc9457SAndroid Build Coastguard Worker       } else if (input < 0) {
343*4bdc9457SAndroid Build Coastguard Worker         input += INT64_C(1) << (s() - 1);
344*4bdc9457SAndroid Build Coastguard Worker       }
345*4bdc9457SAndroid Build Coastguard Worker       inputs[i] = int32_t(input);
346*4bdc9457SAndroid Build Coastguard Worker     }
347*4bdc9457SAndroid Build Coastguard Worker     requantize(inputs.size(), inputs.data(),
348*4bdc9457SAndroid Build Coastguard Worker         scale(), zero_point(), qmin(), qmax(),
349*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
350*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = 0; i <= std::numeric_limits<uint8_t>::max(); i++) {
351*4bdc9457SAndroid Build Coastguard Worker       int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s());
352*4bdc9457SAndroid Build Coastguard Worker       if (input > 0) {
353*4bdc9457SAndroid Build Coastguard Worker         input -= INT64_C(1) << (s() - 1);
354*4bdc9457SAndroid Build Coastguard Worker       } else if (input < 0) {
355*4bdc9457SAndroid Build Coastguard Worker         input += INT64_C(1) << (s() - 1);
356*4bdc9457SAndroid Build Coastguard Worker       }
357*4bdc9457SAndroid Build Coastguard Worker       if (int32_t(input) == input) {
358*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(i, int32_t(outputs[i]))
359*4bdc9457SAndroid Build Coastguard Worker           << "i = " << i << ", input = " << input
360*4bdc9457SAndroid Build Coastguard Worker           << ", s = " << s() << ", zero point = " << zero_point();
361*4bdc9457SAndroid Build Coastguard Worker       }
362*4bdc9457SAndroid Build Coastguard Worker     }
363*4bdc9457SAndroid Build Coastguard Worker   }
364*4bdc9457SAndroid Build Coastguard Worker 
TestDivideByPO2WithRoundingTiesAway(xnn_qs8_requantization_function requantize)365*4bdc9457SAndroid Build Coastguard Worker   void TestDivideByPO2WithRoundingTiesAway(xnn_qs8_requantization_function requantize) {
366*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min());
367*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max());
368*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min());
369*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max());
370*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min());
371*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max());
372*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
373*4bdc9457SAndroid Build Coastguard Worker 
374*4bdc9457SAndroid Build Coastguard Worker     /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */
375*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(s(), 1);
376*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(s(), 32);
377*4bdc9457SAndroid Build Coastguard Worker 
378*4bdc9457SAndroid Build Coastguard Worker     std::vector<int32_t> inputs(256);
379*4bdc9457SAndroid Build Coastguard Worker     std::vector<int8_t> outputs(inputs.size());
380*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) {
381*4bdc9457SAndroid Build Coastguard Worker       int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s());
382*4bdc9457SAndroid Build Coastguard Worker       if (input > 0) {
383*4bdc9457SAndroid Build Coastguard Worker         input -= INT64_C(1) << (s() - 1);
384*4bdc9457SAndroid Build Coastguard Worker       } else if (input < 0) {
385*4bdc9457SAndroid Build Coastguard Worker         input += INT64_C(1) << (s() - 1);
386*4bdc9457SAndroid Build Coastguard Worker       }
387*4bdc9457SAndroid Build Coastguard Worker       inputs[i - std::numeric_limits<int8_t>::min()] = int32_t(input);
388*4bdc9457SAndroid Build Coastguard Worker     }
389*4bdc9457SAndroid Build Coastguard Worker     requantize(inputs.size(), inputs.data(),
390*4bdc9457SAndroid Build Coastguard Worker         scale(), zero_point(), qmin(), qmax(),
391*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
392*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) {
393*4bdc9457SAndroid Build Coastguard Worker       int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s());
394*4bdc9457SAndroid Build Coastguard Worker       if (input > 0) {
395*4bdc9457SAndroid Build Coastguard Worker         input -= INT64_C(1) << (s() - 1);
396*4bdc9457SAndroid Build Coastguard Worker       } else if (input < 0) {
397*4bdc9457SAndroid Build Coastguard Worker         input += INT64_C(1) << (s() - 1);
398*4bdc9457SAndroid Build Coastguard Worker       }
399*4bdc9457SAndroid Build Coastguard Worker       if (int32_t(input) == input) {
400*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(i, int32_t(outputs[i - std::numeric_limits<int8_t>::min()]))
401*4bdc9457SAndroid Build Coastguard Worker           << "i = " << i << ", input = " << input
402*4bdc9457SAndroid Build Coastguard Worker           << ", s = " << s() << ", zero point = " << zero_point();
403*4bdc9457SAndroid Build Coastguard Worker       }
404*4bdc9457SAndroid Build Coastguard Worker     }
405*4bdc9457SAndroid Build Coastguard Worker   }
406*4bdc9457SAndroid Build Coastguard Worker 
TestDivideByPO2WithRoundingTiesUp(xnn_qs8_requantization_function requantize)407*4bdc9457SAndroid Build Coastguard Worker   void TestDivideByPO2WithRoundingTiesUp(xnn_qs8_requantization_function requantize) {
408*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min());
409*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max());
410*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min());
411*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max());
412*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min());
413*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max());
414*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
415*4bdc9457SAndroid Build Coastguard Worker 
416*4bdc9457SAndroid Build Coastguard Worker     /* Note: need s >= 1 to ensure scale = exp2(-s) < 1.0 */
417*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(s(), 1);
418*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(s(), 32);
419*4bdc9457SAndroid Build Coastguard Worker 
420*4bdc9457SAndroid Build Coastguard Worker     std::vector<int32_t> inputs(256);
421*4bdc9457SAndroid Build Coastguard Worker     std::vector<int8_t> outputs(inputs.size());
422*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) {
423*4bdc9457SAndroid Build Coastguard Worker       int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s());
424*4bdc9457SAndroid Build Coastguard Worker       input -= INT64_C(1) << (s() - 1);
425*4bdc9457SAndroid Build Coastguard Worker       inputs[i - std::numeric_limits<int8_t>::min()] = int32_t(input);
426*4bdc9457SAndroid Build Coastguard Worker     }
427*4bdc9457SAndroid Build Coastguard Worker     requantize(inputs.size(), inputs.data(),
428*4bdc9457SAndroid Build Coastguard Worker         scale(), zero_point(), qmin(), qmax(),
429*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
430*4bdc9457SAndroid Build Coastguard Worker     for (int32_t i = std::numeric_limits<int8_t>::min(); i <= std::numeric_limits<int8_t>::max(); i++) {
431*4bdc9457SAndroid Build Coastguard Worker       int64_t input = RequantizationTester::ShiftLeft(i - zero_point(), s());
432*4bdc9457SAndroid Build Coastguard Worker       input -= INT64_C(1) << (s() - 1);
433*4bdc9457SAndroid Build Coastguard Worker       if (int32_t(input) == input) {
434*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(i, int32_t(outputs[i - std::numeric_limits<int8_t>::min()]))
435*4bdc9457SAndroid Build Coastguard Worker           << "i = " << i << ", input = " << input
436*4bdc9457SAndroid Build Coastguard Worker           << ", s = " << s() << ", zero point = " << zero_point();
437*4bdc9457SAndroid Build Coastguard Worker       }
438*4bdc9457SAndroid Build Coastguard Worker     }
439*4bdc9457SAndroid Build Coastguard Worker   }
440*4bdc9457SAndroid Build Coastguard Worker 
TestSpecialCases(xnn_qu8_requantization_function requantize)441*4bdc9457SAndroid Build Coastguard Worker   void TestSpecialCases(xnn_qu8_requantization_function requantize) {
442*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min());
443*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max());
444*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min());
445*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max());
446*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
447*4bdc9457SAndroid Build Coastguard Worker 
448*4bdc9457SAndroid Build Coastguard Worker     std::vector<int32_t> inputs(256);
449*4bdc9457SAndroid Build Coastguard Worker     std::vector<uint8_t> outputs(inputs.size());
450*4bdc9457SAndroid Build Coastguard Worker 
451*4bdc9457SAndroid Build Coastguard Worker     std::fill(inputs.begin(), inputs.end(), std::numeric_limits<int32_t>::min());
452*4bdc9457SAndroid Build Coastguard Worker     for (int32_t zero_point = 0; zero_point <= std::numeric_limits<uint8_t>::max(); zero_point++) {
453*4bdc9457SAndroid Build Coastguard Worker       requantize(
454*4bdc9457SAndroid Build Coastguard Worker           inputs.size(),
455*4bdc9457SAndroid Build Coastguard Worker           inputs.data(),
456*4bdc9457SAndroid Build Coastguard Worker           ldexpf(1.0f, -32) /* scale */,
457*4bdc9457SAndroid Build Coastguard Worker           zero_point /* zero point */,
458*4bdc9457SAndroid Build Coastguard Worker           std::numeric_limits<uint8_t>::min(),
459*4bdc9457SAndroid Build Coastguard Worker           std::numeric_limits<uint8_t>::max(),
460*4bdc9457SAndroid Build Coastguard Worker           outputs.data());
461*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < outputs.size(); i++) {
462*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(std::max(int32_t(int32_t(std::numeric_limits<uint8_t>::min())), zero_point - 1), int32_t(outputs[i]));
463*4bdc9457SAndroid Build Coastguard Worker       }
464*4bdc9457SAndroid Build Coastguard Worker     }
465*4bdc9457SAndroid Build Coastguard Worker 
466*4bdc9457SAndroid Build Coastguard Worker     std::fill(inputs.begin(), inputs.end(), std::numeric_limits<int32_t>::max());
467*4bdc9457SAndroid Build Coastguard Worker     requantize(
468*4bdc9457SAndroid Build Coastguard Worker         inputs.size(),
469*4bdc9457SAndroid Build Coastguard Worker         inputs.data(),
470*4bdc9457SAndroid Build Coastguard Worker         0x1.FFFFFEp-1f /* scale */,
471*4bdc9457SAndroid Build Coastguard Worker         std::numeric_limits<uint8_t>::max() /* zero point */,
472*4bdc9457SAndroid Build Coastguard Worker         std::numeric_limits<uint8_t>::min(),
473*4bdc9457SAndroid Build Coastguard Worker         std::numeric_limits<uint8_t>::max(),
474*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
475*4bdc9457SAndroid Build Coastguard Worker     for (size_t i = 0; i < outputs.size(); i++) {
476*4bdc9457SAndroid Build Coastguard Worker       ASSERT_EQ(std::numeric_limits<uint8_t>::max(), int32_t(outputs[i]));
477*4bdc9457SAndroid Build Coastguard Worker     }
478*4bdc9457SAndroid Build Coastguard Worker   }
479*4bdc9457SAndroid Build Coastguard Worker 
TestSpecialCases(xnn_qs8_requantization_function requantize)480*4bdc9457SAndroid Build Coastguard Worker   void TestSpecialCases(xnn_qs8_requantization_function requantize) {
481*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min());
482*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max());
483*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min());
484*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max());
485*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
486*4bdc9457SAndroid Build Coastguard Worker 
487*4bdc9457SAndroid Build Coastguard Worker     std::vector<int32_t> inputs(256);
488*4bdc9457SAndroid Build Coastguard Worker     std::vector<int8_t> outputs(inputs.size());
489*4bdc9457SAndroid Build Coastguard Worker 
490*4bdc9457SAndroid Build Coastguard Worker     std::fill(inputs.begin(), inputs.end(), std::numeric_limits<int32_t>::min());
491*4bdc9457SAndroid Build Coastguard Worker     for (int32_t zero_point = std::numeric_limits<int8_t>::min();
492*4bdc9457SAndroid Build Coastguard Worker          zero_point <= std::numeric_limits<int8_t>::max();
493*4bdc9457SAndroid Build Coastguard Worker          zero_point++)
494*4bdc9457SAndroid Build Coastguard Worker     {
495*4bdc9457SAndroid Build Coastguard Worker       requantize(
496*4bdc9457SAndroid Build Coastguard Worker           inputs.size(),
497*4bdc9457SAndroid Build Coastguard Worker           inputs.data(),
498*4bdc9457SAndroid Build Coastguard Worker           ldexpf(1.0f, -32) /* scale */,
499*4bdc9457SAndroid Build Coastguard Worker           zero_point,
500*4bdc9457SAndroid Build Coastguard Worker           std::numeric_limits<int8_t>::min(),
501*4bdc9457SAndroid Build Coastguard Worker           std::numeric_limits<int8_t>::max(),
502*4bdc9457SAndroid Build Coastguard Worker           outputs.data());
503*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < outputs.size(); i++) {
504*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(std::max(int32_t(std::numeric_limits<int8_t>::min()), zero_point - 1), int32_t(outputs[i]));
505*4bdc9457SAndroid Build Coastguard Worker       }
506*4bdc9457SAndroid Build Coastguard Worker     }
507*4bdc9457SAndroid Build Coastguard Worker 
508*4bdc9457SAndroid Build Coastguard Worker     std::fill(inputs.begin(), inputs.end(), std::numeric_limits<int32_t>::max());
509*4bdc9457SAndroid Build Coastguard Worker     requantize(
510*4bdc9457SAndroid Build Coastguard Worker         inputs.size(),
511*4bdc9457SAndroid Build Coastguard Worker         inputs.data(),
512*4bdc9457SAndroid Build Coastguard Worker         0x1.FFFFFEp-1f /* scale */,
513*4bdc9457SAndroid Build Coastguard Worker         std::numeric_limits<int8_t>::max() /* zero point */,
514*4bdc9457SAndroid Build Coastguard Worker         std::numeric_limits<int8_t>::min(),
515*4bdc9457SAndroid Build Coastguard Worker         std::numeric_limits<int8_t>::max(),
516*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
517*4bdc9457SAndroid Build Coastguard Worker     for (size_t i = 0; i < outputs.size(); i++) {
518*4bdc9457SAndroid Build Coastguard Worker       ASSERT_EQ(std::numeric_limits<int8_t>::max(), int32_t(outputs[i]));
519*4bdc9457SAndroid Build Coastguard Worker     }
520*4bdc9457SAndroid Build Coastguard Worker   }
521*4bdc9457SAndroid Build Coastguard Worker 
TestRandomCasesRoundToNearestTiesAway(xnn_qu8_requantization_function requantize)522*4bdc9457SAndroid Build Coastguard Worker   void TestRandomCasesRoundToNearestTiesAway(xnn_qu8_requantization_function requantize) {
523*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min());
524*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max());
525*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min());
526*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max());
527*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min());
528*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max());
529*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
530*4bdc9457SAndroid Build Coastguard Worker 
531*4bdc9457SAndroid Build Coastguard Worker     std::random_device random_device;
532*4bdc9457SAndroid Build Coastguard Worker     std::mt19937 rng(random_device());
533*4bdc9457SAndroid Build Coastguard Worker     for (size_t iteration = 0; iteration < iterations(); iteration++) {
534*4bdc9457SAndroid Build Coastguard Worker       auto u8rng =
535*4bdc9457SAndroid Build Coastguard Worker         std::bind(std::uniform_int_distribution<uint32_t>(0, std::numeric_limits<uint8_t>::max()), std::ref(rng));
536*4bdc9457SAndroid Build Coastguard Worker 
537*4bdc9457SAndroid Build Coastguard Worker       std::vector<int32_t> inputs(4096);
538*4bdc9457SAndroid Build Coastguard Worker       std::vector<uint8_t> outputs(inputs.size());
539*4bdc9457SAndroid Build Coastguard Worker 
540*4bdc9457SAndroid Build Coastguard Worker       std::uniform_real_distribution<float> scale_distribution(0x1.000000p-23f, 0x1.FFFFFEp-1f);
541*4bdc9457SAndroid Build Coastguard Worker       const float scale = scale_distribution(rng);
542*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < inputs.size(); i++) {
543*4bdc9457SAndroid Build Coastguard Worker         const uint8_t approximate_output = std::min(std::max(uint8_t(u8rng()), uint8_t(qmin())), uint8_t(qmax()));
544*4bdc9457SAndroid Build Coastguard Worker         const int32_t input = int32_t(double(approximate_output) / double(scale));
545*4bdc9457SAndroid Build Coastguard Worker         inputs[i] = input;
546*4bdc9457SAndroid Build Coastguard Worker       }
547*4bdc9457SAndroid Build Coastguard Worker 
548*4bdc9457SAndroid Build Coastguard Worker       requantize(
549*4bdc9457SAndroid Build Coastguard Worker         inputs.size(), inputs.data(), scale, zero_point(), qmin(), qmax(),
550*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
551*4bdc9457SAndroid Build Coastguard Worker 
552*4bdc9457SAndroid Build Coastguard Worker       /* Ensure that outputs are not all identical, as in this case the test doesn't validate much */
553*4bdc9457SAndroid Build Coastguard Worker       ASSERT_NE(
554*4bdc9457SAndroid Build Coastguard Worker         *std::max_element(outputs.cbegin(), outputs.cend()),
555*4bdc9457SAndroid Build Coastguard Worker         *std::min_element(outputs.cbegin(), outputs.cend()));
556*4bdc9457SAndroid Build Coastguard Worker 
557*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < inputs.size(); i++) {
558*4bdc9457SAndroid Build Coastguard Worker         const uint8_t reference_output = xnn_qu8_requantize_rndna(
559*4bdc9457SAndroid Build Coastguard Worker           inputs[i], scale, zero_point(), qmin(), qmax());
560*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(uint32_t(reference_output), uint32_t(outputs[i]));
561*4bdc9457SAndroid Build Coastguard Worker       }
562*4bdc9457SAndroid Build Coastguard Worker     }
563*4bdc9457SAndroid Build Coastguard Worker   }
564*4bdc9457SAndroid Build Coastguard Worker 
TestRandomCasesRoundToNearestTiesAway(xnn_qs8_requantization_function requantize)565*4bdc9457SAndroid Build Coastguard Worker   void TestRandomCasesRoundToNearestTiesAway(xnn_qs8_requantization_function requantize) {
566*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min());
567*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max());
568*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min());
569*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max());
570*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min());
571*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max());
572*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
573*4bdc9457SAndroid Build Coastguard Worker 
574*4bdc9457SAndroid Build Coastguard Worker     std::random_device random_device;
575*4bdc9457SAndroid Build Coastguard Worker     std::mt19937 rng(random_device());
576*4bdc9457SAndroid Build Coastguard Worker     for (size_t iteration = 0; iteration < iterations(); iteration++) {
577*4bdc9457SAndroid Build Coastguard Worker       auto i8rng = std::bind(
578*4bdc9457SAndroid Build Coastguard Worker         std::uniform_int_distribution<int32_t>(std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max()), std::ref(rng));
579*4bdc9457SAndroid Build Coastguard Worker 
580*4bdc9457SAndroid Build Coastguard Worker       std::vector<int32_t> inputs(4096);
581*4bdc9457SAndroid Build Coastguard Worker       std::vector<int8_t> outputs(inputs.size());
582*4bdc9457SAndroid Build Coastguard Worker 
583*4bdc9457SAndroid Build Coastguard Worker       std::uniform_real_distribution<float> scale_distribution(0x1.000000p-23f, 0x1.FFFFFEp-1f);
584*4bdc9457SAndroid Build Coastguard Worker       const float scale = scale_distribution(rng);
585*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < inputs.size(); i++) {
586*4bdc9457SAndroid Build Coastguard Worker         const int8_t approximate_output = std::min(std::max(int8_t(i8rng()), int8_t(qmin())), int8_t(qmax()));
587*4bdc9457SAndroid Build Coastguard Worker         const int32_t input = int32_t(double(approximate_output) / double(scale));
588*4bdc9457SAndroid Build Coastguard Worker         inputs[i] = input;
589*4bdc9457SAndroid Build Coastguard Worker       }
590*4bdc9457SAndroid Build Coastguard Worker 
591*4bdc9457SAndroid Build Coastguard Worker       requantize(
592*4bdc9457SAndroid Build Coastguard Worker         inputs.size(), inputs.data(), scale, zero_point(), qmin(), qmax(),
593*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
594*4bdc9457SAndroid Build Coastguard Worker 
595*4bdc9457SAndroid Build Coastguard Worker       /* Ensure that outputs are not all identical, as in this case the test doesn't validate much */
596*4bdc9457SAndroid Build Coastguard Worker       ASSERT_NE(
597*4bdc9457SAndroid Build Coastguard Worker         *std::max_element(outputs.cbegin(), outputs.cend()),
598*4bdc9457SAndroid Build Coastguard Worker         *std::min_element(outputs.cbegin(), outputs.cend()));
599*4bdc9457SAndroid Build Coastguard Worker 
600*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < inputs.size(); i++) {
601*4bdc9457SAndroid Build Coastguard Worker         const int8_t reference_output = xnn_qs8_requantize_rndna(
602*4bdc9457SAndroid Build Coastguard Worker           inputs[i], scale, zero_point(), qmin(), qmax());
603*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(int32_t(reference_output), int32_t(outputs[i]));
604*4bdc9457SAndroid Build Coastguard Worker       }
605*4bdc9457SAndroid Build Coastguard Worker     }
606*4bdc9457SAndroid Build Coastguard Worker   }
607*4bdc9457SAndroid Build Coastguard Worker 
TestRandomCasesRoundToNearestTiesUp(xnn_qs8_requantization_function requantize)608*4bdc9457SAndroid Build Coastguard Worker   void TestRandomCasesRoundToNearestTiesUp(xnn_qs8_requantization_function requantize) {
609*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min());
610*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max());
611*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min());
612*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max());
613*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min());
614*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max());
615*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
616*4bdc9457SAndroid Build Coastguard Worker 
617*4bdc9457SAndroid Build Coastguard Worker     std::random_device random_device;
618*4bdc9457SAndroid Build Coastguard Worker     std::mt19937 rng(random_device());
619*4bdc9457SAndroid Build Coastguard Worker     for (size_t iteration = 0; iteration < iterations(); iteration++) {
620*4bdc9457SAndroid Build Coastguard Worker       auto i8rng = std::bind(
621*4bdc9457SAndroid Build Coastguard Worker         std::uniform_int_distribution<int32_t>(std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max()), std::ref(rng));
622*4bdc9457SAndroid Build Coastguard Worker 
623*4bdc9457SAndroid Build Coastguard Worker       std::vector<int32_t> inputs(4096);
624*4bdc9457SAndroid Build Coastguard Worker       std::vector<int8_t> outputs(inputs.size());
625*4bdc9457SAndroid Build Coastguard Worker 
626*4bdc9457SAndroid Build Coastguard Worker       std::uniform_real_distribution<float> scale_distribution(0x1.000000p-23f, 0x1.FFFFFEp-1f);
627*4bdc9457SAndroid Build Coastguard Worker       const float scale = scale_distribution(rng);
628*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < inputs.size(); i++) {
629*4bdc9457SAndroid Build Coastguard Worker         const int8_t approximate_output = std::min(std::max(int8_t(i8rng()), int8_t(qmin())), int8_t(qmax()));
630*4bdc9457SAndroid Build Coastguard Worker         const int32_t input = int32_t(double(approximate_output) / double(scale));
631*4bdc9457SAndroid Build Coastguard Worker         inputs[i] = input;
632*4bdc9457SAndroid Build Coastguard Worker       }
633*4bdc9457SAndroid Build Coastguard Worker 
634*4bdc9457SAndroid Build Coastguard Worker       requantize(
635*4bdc9457SAndroid Build Coastguard Worker         inputs.size(), inputs.data(), scale, zero_point(), qmin(), qmax(),
636*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
637*4bdc9457SAndroid Build Coastguard Worker 
638*4bdc9457SAndroid Build Coastguard Worker       /* Ensure that outputs are not all identical, as in this case the test doesn't validate much */
639*4bdc9457SAndroid Build Coastguard Worker       ASSERT_NE(
640*4bdc9457SAndroid Build Coastguard Worker         *std::max_element(outputs.cbegin(), outputs.cend()),
641*4bdc9457SAndroid Build Coastguard Worker         *std::min_element(outputs.cbegin(), outputs.cend()));
642*4bdc9457SAndroid Build Coastguard Worker 
643*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < inputs.size(); i++) {
644*4bdc9457SAndroid Build Coastguard Worker         const int8_t reference_output = xnn_qs8_requantize_rndnu(
645*4bdc9457SAndroid Build Coastguard Worker           inputs[i], scale, zero_point(), qmin(), qmax());
646*4bdc9457SAndroid Build Coastguard Worker         ASSERT_EQ(int32_t(reference_output), int32_t(outputs[i]));
647*4bdc9457SAndroid Build Coastguard Worker       }
648*4bdc9457SAndroid Build Coastguard Worker     }
649*4bdc9457SAndroid Build Coastguard Worker   }
650*4bdc9457SAndroid Build Coastguard Worker 
TestRandomCasesApproximate(xnn_qu8_requantization_function requantize)651*4bdc9457SAndroid Build Coastguard Worker   void TestRandomCasesApproximate(xnn_qu8_requantization_function requantize) {
652*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<uint8_t>::min());
653*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<uint8_t>::max());
654*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<uint8_t>::min());
655*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<uint8_t>::max());
656*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<uint8_t>::min());
657*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<uint8_t>::max());
658*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
659*4bdc9457SAndroid Build Coastguard Worker 
660*4bdc9457SAndroid Build Coastguard Worker     std::random_device random_device;
661*4bdc9457SAndroid Build Coastguard Worker     std::mt19937 rng(random_device());
662*4bdc9457SAndroid Build Coastguard Worker     for (size_t iteration = 0; iteration < iterations(); iteration++) {
663*4bdc9457SAndroid Build Coastguard Worker       auto u8rng =
664*4bdc9457SAndroid Build Coastguard Worker         std::bind(std::uniform_int_distribution<uint32_t>(0, std::numeric_limits<uint8_t>::max()), std::ref(rng));
665*4bdc9457SAndroid Build Coastguard Worker 
666*4bdc9457SAndroid Build Coastguard Worker       std::vector<int32_t> inputs(4096);
667*4bdc9457SAndroid Build Coastguard Worker       std::vector<uint8_t> outputs(inputs.size());
668*4bdc9457SAndroid Build Coastguard Worker 
669*4bdc9457SAndroid Build Coastguard Worker       std::uniform_real_distribution<float> scale_distribution(0x1.000000p-23f, 0x1.FFFFFEp-1f);
670*4bdc9457SAndroid Build Coastguard Worker       const float scale = scale_distribution(rng);
671*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < inputs.size(); i++) {
672*4bdc9457SAndroid Build Coastguard Worker         const uint8_t approximate_output = std::min(std::max(uint8_t(u8rng()), uint8_t(qmin())), uint8_t(qmax()));
673*4bdc9457SAndroid Build Coastguard Worker         const int32_t input = int32_t(double(approximate_output) / double(scale));
674*4bdc9457SAndroid Build Coastguard Worker         inputs[i] = input;
675*4bdc9457SAndroid Build Coastguard Worker       }
676*4bdc9457SAndroid Build Coastguard Worker 
677*4bdc9457SAndroid Build Coastguard Worker       requantize(
678*4bdc9457SAndroid Build Coastguard Worker         inputs.size(), inputs.data(), scale, zero_point(), qmin(), qmax(),
679*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
680*4bdc9457SAndroid Build Coastguard Worker 
681*4bdc9457SAndroid Build Coastguard Worker       /* Ensure that outputs are not all identical, as in this case Test doesn't validate much */
682*4bdc9457SAndroid Build Coastguard Worker       ASSERT_NE(
683*4bdc9457SAndroid Build Coastguard Worker         *std::max_element(outputs.cbegin(), outputs.cend()),
684*4bdc9457SAndroid Build Coastguard Worker         *std::min_element(outputs.cbegin(), outputs.cend()));
685*4bdc9457SAndroid Build Coastguard Worker 
686*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < inputs.size(); i++) {
687*4bdc9457SAndroid Build Coastguard Worker         const double reference_output = RequantizationTester::RequantizeApproximate(
688*4bdc9457SAndroid Build Coastguard Worker           inputs[i], scale, uint8_t(zero_point()), uint8_t(qmin()), uint8_t(qmax()));
689*4bdc9457SAndroid Build Coastguard Worker         ASSERT_LE(std::abs(reference_output - double(outputs[i])), 0.55)
690*4bdc9457SAndroid Build Coastguard Worker           << "input = " << inputs[i] << ", output = " << int32_t(outputs[i])
691*4bdc9457SAndroid Build Coastguard Worker           << ", reference output = " << reference_output;
692*4bdc9457SAndroid Build Coastguard Worker       }
693*4bdc9457SAndroid Build Coastguard Worker     }
694*4bdc9457SAndroid Build Coastguard Worker   }
695*4bdc9457SAndroid Build Coastguard Worker 
TestRandomCasesApproximate(xnn_qs8_requantization_function requantize)696*4bdc9457SAndroid Build Coastguard Worker   void TestRandomCasesApproximate(xnn_qs8_requantization_function requantize) {
697*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(zero_point(), std::numeric_limits<int8_t>::min());
698*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(zero_point(), std::numeric_limits<int8_t>::max());
699*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmin(), std::numeric_limits<int8_t>::min());
700*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmin(), std::numeric_limits<int8_t>::max());
701*4bdc9457SAndroid Build Coastguard Worker     ASSERT_GE(qmax(), std::numeric_limits<int8_t>::min());
702*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LE(qmax(), std::numeric_limits<int8_t>::max());
703*4bdc9457SAndroid Build Coastguard Worker     ASSERT_LT(qmin(), qmax());
704*4bdc9457SAndroid Build Coastguard Worker 
705*4bdc9457SAndroid Build Coastguard Worker     std::random_device random_device;
706*4bdc9457SAndroid Build Coastguard Worker     std::mt19937 rng(random_device());
707*4bdc9457SAndroid Build Coastguard Worker     for (size_t iteration = 0; iteration < iterations(); iteration++) {
708*4bdc9457SAndroid Build Coastguard Worker       auto i8rng = std::bind(
709*4bdc9457SAndroid Build Coastguard Worker         std::uniform_int_distribution<int32_t>(std::numeric_limits<int8_t>::min(), std::numeric_limits<int8_t>::max()), std::ref(rng));
710*4bdc9457SAndroid Build Coastguard Worker 
711*4bdc9457SAndroid Build Coastguard Worker       std::vector<int32_t> inputs(4096);
712*4bdc9457SAndroid Build Coastguard Worker       std::vector<int8_t> outputs(inputs.size());
713*4bdc9457SAndroid Build Coastguard Worker 
714*4bdc9457SAndroid Build Coastguard Worker       std::uniform_real_distribution<float> scale_distribution(0x1.000000p-23f, 0x1.FFFFFEp-1f);
715*4bdc9457SAndroid Build Coastguard Worker       const float scale = scale_distribution(rng);
716*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < inputs.size(); i++) {
717*4bdc9457SAndroid Build Coastguard Worker         const int8_t approximate_output = std::min(std::max(int8_t(i8rng()), int8_t(qmin())), int8_t(qmax()));
718*4bdc9457SAndroid Build Coastguard Worker         const int32_t input = int32_t(double(approximate_output) / double(scale));
719*4bdc9457SAndroid Build Coastguard Worker         inputs[i] = input;
720*4bdc9457SAndroid Build Coastguard Worker       }
721*4bdc9457SAndroid Build Coastguard Worker 
722*4bdc9457SAndroid Build Coastguard Worker       requantize(
723*4bdc9457SAndroid Build Coastguard Worker         inputs.size(), inputs.data(), scale, zero_point(), qmin(), qmax(),
724*4bdc9457SAndroid Build Coastguard Worker         outputs.data());
725*4bdc9457SAndroid Build Coastguard Worker 
726*4bdc9457SAndroid Build Coastguard Worker       /* Ensure that outputs are not all identical, as in this case Test doesn't validate much */
727*4bdc9457SAndroid Build Coastguard Worker       ASSERT_NE(
728*4bdc9457SAndroid Build Coastguard Worker         *std::max_element(outputs.cbegin(), outputs.cend()),
729*4bdc9457SAndroid Build Coastguard Worker         *std::min_element(outputs.cbegin(), outputs.cend()));
730*4bdc9457SAndroid Build Coastguard Worker 
731*4bdc9457SAndroid Build Coastguard Worker       for (size_t i = 0; i < inputs.size(); i++) {
732*4bdc9457SAndroid Build Coastguard Worker         const double reference_output = RequantizationTester::RequantizeApproximate(
733*4bdc9457SAndroid Build Coastguard Worker           inputs[i], scale, int8_t(zero_point()), int8_t(qmin()), int8_t(qmax()));
734*4bdc9457SAndroid Build Coastguard Worker         ASSERT_LE(std::abs(reference_output - double(outputs[i])), 0.55)
735*4bdc9457SAndroid Build Coastguard Worker           << "input = " << inputs[i] << ", output = " << int32_t(outputs[i])
736*4bdc9457SAndroid Build Coastguard Worker           << ", reference output = " << reference_output;
737*4bdc9457SAndroid Build Coastguard Worker       }
738*4bdc9457SAndroid Build Coastguard Worker     }
739*4bdc9457SAndroid Build Coastguard Worker   }
740*4bdc9457SAndroid Build Coastguard Worker 
ShiftLeft(int64_t w,uint32_t n)741*4bdc9457SAndroid Build Coastguard Worker   static inline int64_t ShiftLeft(int64_t w, uint32_t n) {
742*4bdc9457SAndroid Build Coastguard Worker     return (int64_t) ((uint64_t) w << n);
743*4bdc9457SAndroid Build Coastguard Worker   }
744*4bdc9457SAndroid Build Coastguard Worker 
RequantizeApproximate(int32_t value,float scale,uint8_t zero_point,uint8_t qmin,uint8_t qmax)745*4bdc9457SAndroid Build Coastguard Worker   static inline double RequantizeApproximate(
746*4bdc9457SAndroid Build Coastguard Worker     int32_t value,
747*4bdc9457SAndroid Build Coastguard Worker     float scale,
748*4bdc9457SAndroid Build Coastguard Worker     uint8_t zero_point,
749*4bdc9457SAndroid Build Coastguard Worker     uint8_t qmin,
750*4bdc9457SAndroid Build Coastguard Worker     uint8_t qmax)
751*4bdc9457SAndroid Build Coastguard Worker   {
752*4bdc9457SAndroid Build Coastguard Worker     assert(scale < 1.0f);
753*4bdc9457SAndroid Build Coastguard Worker     assert(scale >= 0x1.0p-32f);
754*4bdc9457SAndroid Build Coastguard Worker 
755*4bdc9457SAndroid Build Coastguard Worker     return std::min(std::max(double(value) * double(scale) + double(zero_point), double(qmin)), double(qmax));
756*4bdc9457SAndroid Build Coastguard Worker   }
757*4bdc9457SAndroid Build Coastguard Worker 
RequantizeApproximate(int32_t value,float scale,int8_t zero_point,int8_t qmin,int8_t qmax)758*4bdc9457SAndroid Build Coastguard Worker   static inline double RequantizeApproximate(
759*4bdc9457SAndroid Build Coastguard Worker     int32_t value,
760*4bdc9457SAndroid Build Coastguard Worker     float scale,
761*4bdc9457SAndroid Build Coastguard Worker     int8_t zero_point,
762*4bdc9457SAndroid Build Coastguard Worker     int8_t qmin,
763*4bdc9457SAndroid Build Coastguard Worker     int8_t qmax)
764*4bdc9457SAndroid Build Coastguard Worker   {
765*4bdc9457SAndroid Build Coastguard Worker     assert(scale < 1.0f);
766*4bdc9457SAndroid Build Coastguard Worker     assert(scale >= 0x1.0p-32f);
767*4bdc9457SAndroid Build Coastguard Worker 
768*4bdc9457SAndroid Build Coastguard Worker     return std::min(std::max(double(value) * double(scale) + double(zero_point), double(qmin)), double(qmax));
769*4bdc9457SAndroid Build Coastguard Worker   }
770*4bdc9457SAndroid Build Coastguard Worker 
771*4bdc9457SAndroid Build Coastguard Worker  private:
772*4bdc9457SAndroid Build Coastguard Worker   uint32_t s_{1};
773*4bdc9457SAndroid Build Coastguard Worker   int32_t zero_point_{0};
774*4bdc9457SAndroid Build Coastguard Worker   int16_t qmin_{std::numeric_limits<int16_t>::min()};
775*4bdc9457SAndroid Build Coastguard Worker   int16_t qmax_{std::numeric_limits<int16_t>::max()};
776*4bdc9457SAndroid Build Coastguard Worker   size_t iterations_{1};
777*4bdc9457SAndroid Build Coastguard Worker };
778