xref: /aosp_15_r20/external/swiftshader/tests/MathUnitTests/unittests.cpp (revision 03ce13f70fcc45d86ee91b7ee4cab1936a95046e)
1*03ce13f7SAndroid Build Coastguard Worker // Copyright 2019 The SwiftShader Authors. All Rights Reserved.
2*03ce13f7SAndroid Build Coastguard Worker //
3*03ce13f7SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*03ce13f7SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*03ce13f7SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*03ce13f7SAndroid Build Coastguard Worker //
7*03ce13f7SAndroid Build Coastguard Worker //    http://www.apache.org/licenses/LICENSE-2.0
8*03ce13f7SAndroid Build Coastguard Worker //
9*03ce13f7SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*03ce13f7SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*03ce13f7SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*03ce13f7SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*03ce13f7SAndroid Build Coastguard Worker // limitations under the License.
14*03ce13f7SAndroid Build Coastguard Worker 
15*03ce13f7SAndroid Build Coastguard Worker #include "System/CPUID.hpp"
16*03ce13f7SAndroid Build Coastguard Worker #include "System/Half.hpp"
17*03ce13f7SAndroid Build Coastguard Worker #include "System/Math.hpp"
18*03ce13f7SAndroid Build Coastguard Worker 
19*03ce13f7SAndroid Build Coastguard Worker #include <gmock/gmock.h>
20*03ce13f7SAndroid Build Coastguard Worker #include <gtest/gtest.h>
21*03ce13f7SAndroid Build Coastguard Worker 
22*03ce13f7SAndroid Build Coastguard Worker #include <cstdlib>
23*03ce13f7SAndroid Build Coastguard Worker #include <cmath>
24*03ce13f7SAndroid Build Coastguard Worker 
25*03ce13f7SAndroid Build Coastguard Worker using std::isnan;
26*03ce13f7SAndroid Build Coastguard Worker using std::isinf;
27*03ce13f7SAndroid Build Coastguard Worker using std::signbit;
28*03ce13f7SAndroid Build Coastguard Worker 
29*03ce13f7SAndroid Build Coastguard Worker using namespace sw;
30*03ce13f7SAndroid Build Coastguard Worker 
31*03ce13f7SAndroid Build Coastguard Worker // Implementation of frexp() which satisfies C++ <cmath> requirements.
fast_frexp(float val,int * exp)32*03ce13f7SAndroid Build Coastguard Worker float fast_frexp(float val, int *exp)
33*03ce13f7SAndroid Build Coastguard Worker {
34*03ce13f7SAndroid Build Coastguard Worker 	int isNotZero = (val != 0.0f) ? 0xFFFFFFFF : 0x00000000;
35*03ce13f7SAndroid Build Coastguard Worker 	int v = bit_cast<int>(val);
36*03ce13f7SAndroid Build Coastguard Worker 	int isInfOrNaN = (v & 0x7F800000) == 0x7F800000 ? 0xFFFFFFFF : 0x00000000;
37*03ce13f7SAndroid Build Coastguard Worker 
38*03ce13f7SAndroid Build Coastguard Worker 	// When val is a subnormal value we can't directly use its mantissa to construct the significand in
39*03ce13f7SAndroid Build Coastguard Worker 	// the range [0.5, 1.0). We need to multiply it by a factor that makes it normalized. For large
40*03ce13f7SAndroid Build Coastguard Worker 	// values the factor must avoid overflow to inifity.
41*03ce13f7SAndroid Build Coastguard Worker 	int factor = ((127 + 23) << 23) - (v & 0x3F800000);
42*03ce13f7SAndroid Build Coastguard Worker 	int nval = bit_cast<int>(val * bit_cast<float>(factor));
43*03ce13f7SAndroid Build Coastguard Worker 
44*03ce13f7SAndroid Build Coastguard Worker 	// Extract the exponent of the normalized value and subtract the exponent of the normalizing factor.
45*03ce13f7SAndroid Build Coastguard Worker 	int exponent = ((((nval & 0x7F800000) - factor) >> 23) + 1) & isNotZero;
46*03ce13f7SAndroid Build Coastguard Worker 
47*03ce13f7SAndroid Build Coastguard Worker 	// Substitute the exponent of 0.5f (if not zero) to obtain the significand.
48*03ce13f7SAndroid Build Coastguard Worker 	float significand = bit_cast<float>((nval & 0x807FFFFF) | (0x3F000000 & isNotZero) | (0x7F800000 & isInfOrNaN));
49*03ce13f7SAndroid Build Coastguard Worker 
50*03ce13f7SAndroid Build Coastguard Worker 	*exp = exponent;
51*03ce13f7SAndroid Build Coastguard Worker 	return significand;
52*03ce13f7SAndroid Build Coastguard Worker }
53*03ce13f7SAndroid Build Coastguard Worker 
TEST(MathTest,Frexp)54*03ce13f7SAndroid Build Coastguard Worker TEST(MathTest, Frexp)
55*03ce13f7SAndroid Build Coastguard Worker {
56*03ce13f7SAndroid Build Coastguard Worker 	for(bool flush : { false, true })
57*03ce13f7SAndroid Build Coastguard Worker 	{
58*03ce13f7SAndroid Build Coastguard Worker 		CPUID::setDenormalsAreZero(flush);
59*03ce13f7SAndroid Build Coastguard Worker 		CPUID::setFlushToZero(flush);
60*03ce13f7SAndroid Build Coastguard Worker 
61*03ce13f7SAndroid Build Coastguard Worker 		std::vector<float> a = {
62*03ce13f7SAndroid Build Coastguard Worker 			2.3f,
63*03ce13f7SAndroid Build Coastguard Worker 			0.1f,
64*03ce13f7SAndroid Build Coastguard Worker 			0.7f,
65*03ce13f7SAndroid Build Coastguard Worker 			1.7f,
66*03ce13f7SAndroid Build Coastguard Worker 			0.0f,
67*03ce13f7SAndroid Build Coastguard Worker 			-2.3f,
68*03ce13f7SAndroid Build Coastguard Worker 			-0.1f,
69*03ce13f7SAndroid Build Coastguard Worker 			-0.7f,
70*03ce13f7SAndroid Build Coastguard Worker 			-1.7f,
71*03ce13f7SAndroid Build Coastguard Worker 			-0.0f,
72*03ce13f7SAndroid Build Coastguard Worker 			100000000.0f,
73*03ce13f7SAndroid Build Coastguard Worker 			-100000000.0f,
74*03ce13f7SAndroid Build Coastguard Worker 			0.000000001f,
75*03ce13f7SAndroid Build Coastguard Worker 			-0.000000001f,
76*03ce13f7SAndroid Build Coastguard Worker 			FLT_MIN,
77*03ce13f7SAndroid Build Coastguard Worker 			-FLT_MIN,
78*03ce13f7SAndroid Build Coastguard Worker 			FLT_MAX,
79*03ce13f7SAndroid Build Coastguard Worker 			-FLT_MAX,
80*03ce13f7SAndroid Build Coastguard Worker 			FLT_TRUE_MIN,
81*03ce13f7SAndroid Build Coastguard Worker 			-FLT_TRUE_MIN,
82*03ce13f7SAndroid Build Coastguard Worker 			INFINITY,
83*03ce13f7SAndroid Build Coastguard Worker 			-INFINITY,
84*03ce13f7SAndroid Build Coastguard Worker 			NAN,
85*03ce13f7SAndroid Build Coastguard Worker 			bit_cast<float>(0x007FFFFF),  // Largest subnormal
86*03ce13f7SAndroid Build Coastguard Worker 			bit_cast<float>(0x807FFFFF),
87*03ce13f7SAndroid Build Coastguard Worker 			bit_cast<float>(0x00000001),  // Smallest subnormal
88*03ce13f7SAndroid Build Coastguard Worker 			bit_cast<float>(0x80000001),
89*03ce13f7SAndroid Build Coastguard Worker 		};
90*03ce13f7SAndroid Build Coastguard Worker 
91*03ce13f7SAndroid Build Coastguard Worker 		for(float f : a)
92*03ce13f7SAndroid Build Coastguard Worker 		{
93*03ce13f7SAndroid Build Coastguard Worker 			int exp = -1000;
94*03ce13f7SAndroid Build Coastguard Worker 			float sig = fast_frexp(f, &exp);
95*03ce13f7SAndroid Build Coastguard Worker 
96*03ce13f7SAndroid Build Coastguard Worker 			if(f == 0.0f)  // Could be subnormal if `flush` is true
97*03ce13f7SAndroid Build Coastguard Worker 			{
98*03ce13f7SAndroid Build Coastguard Worker 				// We don't rely on std::frexp here to produce a reference result because it may
99*03ce13f7SAndroid Build Coastguard Worker 				// return non-zero significands and exponents for subnormal arguments., while our
100*03ce13f7SAndroid Build Coastguard Worker 				// implementation is meant to respect denormals-are-zero / flush-to-zero.
101*03ce13f7SAndroid Build Coastguard Worker 
102*03ce13f7SAndroid Build Coastguard Worker 				ASSERT_EQ(sig, 0.0f) << "Argument: " << std::hexfloat << f;
103*03ce13f7SAndroid Build Coastguard Worker 				ASSERT_TRUE(signbit(sig) == signbit(f)) << "Argument: " << std::hexfloat << f;
104*03ce13f7SAndroid Build Coastguard Worker 				ASSERT_EQ(exp, 0) << "Argument: " << std::hexfloat << f;
105*03ce13f7SAndroid Build Coastguard Worker 			}
106*03ce13f7SAndroid Build Coastguard Worker 			else
107*03ce13f7SAndroid Build Coastguard Worker 			{
108*03ce13f7SAndroid Build Coastguard Worker 				int ref_exp = -1000;
109*03ce13f7SAndroid Build Coastguard Worker 				float ref_sig = std::frexp(f, &ref_exp);
110*03ce13f7SAndroid Build Coastguard Worker 
111*03ce13f7SAndroid Build Coastguard Worker 				if(!isnan(f))
112*03ce13f7SAndroid Build Coastguard Worker 				{
113*03ce13f7SAndroid Build Coastguard Worker 					ASSERT_EQ(sig, ref_sig) << "Argument: " << std::hexfloat << f;
114*03ce13f7SAndroid Build Coastguard Worker 				}
115*03ce13f7SAndroid Build Coastguard Worker 				else
116*03ce13f7SAndroid Build Coastguard Worker 				{
117*03ce13f7SAndroid Build Coastguard Worker 					ASSERT_TRUE(isnan(sig)) << "Significand: " << std::hexfloat << sig;
118*03ce13f7SAndroid Build Coastguard Worker 				}
119*03ce13f7SAndroid Build Coastguard Worker 
120*03ce13f7SAndroid Build Coastguard Worker 				if(!isinf(f) && !isnan(f))  // If the argument is NaN or Inf the exponent is unspecified.
121*03ce13f7SAndroid Build Coastguard Worker 				{
122*03ce13f7SAndroid Build Coastguard Worker 					ASSERT_EQ(exp, ref_exp) << "Argument: " << std::hexfloat << f;
123*03ce13f7SAndroid Build Coastguard Worker 				}
124*03ce13f7SAndroid Build Coastguard Worker 			}
125*03ce13f7SAndroid Build Coastguard Worker 		}
126*03ce13f7SAndroid Build Coastguard Worker 	}
127*03ce13f7SAndroid Build Coastguard Worker }
128*03ce13f7SAndroid Build Coastguard Worker 
129*03ce13f7SAndroid Build Coastguard Worker // Returns the whole-number ULP error of `a` relative to `x`.
130*03ce13f7SAndroid Build Coastguard Worker // Use the doouble-precision version below. This just illustrates the principle.
ULP_32(float x,float a)131*03ce13f7SAndroid Build Coastguard Worker [[deprecated]] float ULP_32(float x, float a)
132*03ce13f7SAndroid Build Coastguard Worker {
133*03ce13f7SAndroid Build Coastguard Worker 	// Flip the last mantissa bit to compute the 'unit in the last place' error.
134*03ce13f7SAndroid Build Coastguard Worker 	float x1 = bit_cast<float>(bit_cast<uint32_t>(x) ^ 0x00000001);
135*03ce13f7SAndroid Build Coastguard Worker 	float ulp = abs(x1 - x);
136*03ce13f7SAndroid Build Coastguard Worker 
137*03ce13f7SAndroid Build Coastguard Worker 	return abs(a - x) / ulp;
138*03ce13f7SAndroid Build Coastguard Worker }
139*03ce13f7SAndroid Build Coastguard Worker 
ULP_32(double x,double a)140*03ce13f7SAndroid Build Coastguard Worker double ULP_32(double x, double a)
141*03ce13f7SAndroid Build Coastguard Worker {
142*03ce13f7SAndroid Build Coastguard Worker 	// binary64 has 52 mantissa bits, while binary32 has 23, so the ULP for the latter is 29 bits shifted.
143*03ce13f7SAndroid Build Coastguard Worker 	double x1 = bit_cast<double>(bit_cast<uint64_t>(x) ^ 0x0000000020000000ull);
144*03ce13f7SAndroid Build Coastguard Worker 	double ulp = abs(x1 - x);
145*03ce13f7SAndroid Build Coastguard Worker 
146*03ce13f7SAndroid Build Coastguard Worker 	return abs(a - x) / ulp;
147*03ce13f7SAndroid Build Coastguard Worker }
148*03ce13f7SAndroid Build Coastguard Worker 
ULP_16(float x,float a)149*03ce13f7SAndroid Build Coastguard Worker float ULP_16(float x, float a)
150*03ce13f7SAndroid Build Coastguard Worker {
151*03ce13f7SAndroid Build Coastguard Worker 	// binary32 has 23 mantissa bits, while binary16 has 10, so the ULP for the latter is 13 bits shifted.
152*03ce13f7SAndroid Build Coastguard Worker 	double x1 = bit_cast<float>(bit_cast<uint32_t>(x) ^ 0x00002000);
153*03ce13f7SAndroid Build Coastguard Worker 	float ulp = abs(x1 - x);
154*03ce13f7SAndroid Build Coastguard Worker 
155*03ce13f7SAndroid Build Coastguard Worker 	return abs(a - x) / ulp;
156*03ce13f7SAndroid Build Coastguard Worker }
157*03ce13f7SAndroid Build Coastguard Worker 
158*03ce13f7SAndroid Build Coastguard Worker // lolremez --float -d 2 -r "0:2^23" "(log2(x/2^23+1)-x/2^23)/x" "1/x"
159*03ce13f7SAndroid Build Coastguard Worker // ULP-16: 0.797363281, abs: 0.0991751999
f(float x)160*03ce13f7SAndroid Build Coastguard Worker float f(float x)
161*03ce13f7SAndroid Build Coastguard Worker {
162*03ce13f7SAndroid Build Coastguard Worker 	float u = 2.8017103e-22f;
163*03ce13f7SAndroid Build Coastguard Worker 	u = u * x + -8.373131e-15f;
164*03ce13f7SAndroid Build Coastguard Worker 	return u * x + 5.0615534e-8f;
165*03ce13f7SAndroid Build Coastguard Worker }
166*03ce13f7SAndroid Build Coastguard Worker 
Log2Relaxed(float x)167*03ce13f7SAndroid Build Coastguard Worker float Log2Relaxed(float x)
168*03ce13f7SAndroid Build Coastguard Worker {
169*03ce13f7SAndroid Build Coastguard Worker 	// Reinterpretation as an integer provides a piecewise linear
170*03ce13f7SAndroid Build Coastguard Worker 	// approximation of log2(). Scale to the radix and subtract exponent bias.
171*03ce13f7SAndroid Build Coastguard Worker 	int im = bit_cast<int>(x);
172*03ce13f7SAndroid Build Coastguard Worker 	float y = (float)im * (1.0f / (1 << 23)) - 127.0f;
173*03ce13f7SAndroid Build Coastguard Worker 
174*03ce13f7SAndroid Build Coastguard Worker 	// Handle log2(inf) = inf.
175*03ce13f7SAndroid Build Coastguard Worker 	if(im == 0x7F800000) y = INFINITY;
176*03ce13f7SAndroid Build Coastguard Worker 
177*03ce13f7SAndroid Build Coastguard Worker 	float m = (float)(im & 0x007FFFFF);  // Unnormalized mantissa of x.
178*03ce13f7SAndroid Build Coastguard Worker 
179*03ce13f7SAndroid Build Coastguard Worker 	// Add a polynomial approximation of log2(m+1)-m to the result's mantissa.
180*03ce13f7SAndroid Build Coastguard Worker 	return f(m) * m + y;
181*03ce13f7SAndroid Build Coastguard Worker }
182*03ce13f7SAndroid Build Coastguard Worker 
TEST(MathTest,Log2RelaxedExhaustive)183*03ce13f7SAndroid Build Coastguard Worker TEST(MathTest, Log2RelaxedExhaustive)
184*03ce13f7SAndroid Build Coastguard Worker {
185*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setDenormalsAreZero(true);
186*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setFlushToZero(true);
187*03ce13f7SAndroid Build Coastguard Worker 
188*03ce13f7SAndroid Build Coastguard Worker 	float worst_margin = 0;
189*03ce13f7SAndroid Build Coastguard Worker 	float worst_ulp = 0;
190*03ce13f7SAndroid Build Coastguard Worker 	float worst_x = 0;
191*03ce13f7SAndroid Build Coastguard Worker 	float worst_val = 0;
192*03ce13f7SAndroid Build Coastguard Worker 	float worst_ref = 0;
193*03ce13f7SAndroid Build Coastguard Worker 
194*03ce13f7SAndroid Build Coastguard Worker 	float worst_abs = 0;
195*03ce13f7SAndroid Build Coastguard Worker 
196*03ce13f7SAndroid Build Coastguard Worker 	for(float x = 0.0f; x <= INFINITY; x = inc(x))
197*03ce13f7SAndroid Build Coastguard Worker 	{
198*03ce13f7SAndroid Build Coastguard Worker 		float val = Log2Relaxed(x);
199*03ce13f7SAndroid Build Coastguard Worker 
200*03ce13f7SAndroid Build Coastguard Worker 		double ref = log2((double)x);
201*03ce13f7SAndroid Build Coastguard Worker 
202*03ce13f7SAndroid Build Coastguard Worker 		if(ref == (int)ref)
203*03ce13f7SAndroid Build Coastguard Worker 		{
204*03ce13f7SAndroid Build Coastguard Worker 			ASSERT_EQ(val, ref);
205*03ce13f7SAndroid Build Coastguard Worker 		}
206*03ce13f7SAndroid Build Coastguard Worker 		else if(x >= 0.5f && x <= 2.0f)
207*03ce13f7SAndroid Build Coastguard Worker 		{
208*03ce13f7SAndroid Build Coastguard Worker 			const float tolerance = pow(2.0f, -7.0f);  // Absolute
209*03ce13f7SAndroid Build Coastguard Worker 
210*03ce13f7SAndroid Build Coastguard Worker 			float margin = abs(val - ref) / tolerance;
211*03ce13f7SAndroid Build Coastguard Worker 
212*03ce13f7SAndroid Build Coastguard Worker 			if(margin > worst_abs)
213*03ce13f7SAndroid Build Coastguard Worker 			{
214*03ce13f7SAndroid Build Coastguard Worker 				worst_abs = margin;
215*03ce13f7SAndroid Build Coastguard Worker 			}
216*03ce13f7SAndroid Build Coastguard Worker 		}
217*03ce13f7SAndroid Build Coastguard Worker 		else
218*03ce13f7SAndroid Build Coastguard Worker 		{
219*03ce13f7SAndroid Build Coastguard Worker 			const float tolerance = 3;  // ULP
220*03ce13f7SAndroid Build Coastguard Worker 
221*03ce13f7SAndroid Build Coastguard Worker 			float ulp = (float)ULP_16(ref, (double)val);
222*03ce13f7SAndroid Build Coastguard Worker 			float margin = ulp / tolerance;
223*03ce13f7SAndroid Build Coastguard Worker 
224*03ce13f7SAndroid Build Coastguard Worker 			if(margin > worst_margin)
225*03ce13f7SAndroid Build Coastguard Worker 			{
226*03ce13f7SAndroid Build Coastguard Worker 				worst_margin = margin;
227*03ce13f7SAndroid Build Coastguard Worker 				worst_ulp = ulp;
228*03ce13f7SAndroid Build Coastguard Worker 				worst_x = x;
229*03ce13f7SAndroid Build Coastguard Worker 				worst_val = val;
230*03ce13f7SAndroid Build Coastguard Worker 				worst_ref = ref;
231*03ce13f7SAndroid Build Coastguard Worker 			}
232*03ce13f7SAndroid Build Coastguard Worker 		}
233*03ce13f7SAndroid Build Coastguard Worker 	}
234*03ce13f7SAndroid Build Coastguard Worker 
235*03ce13f7SAndroid Build Coastguard Worker 	ASSERT_TRUE(worst_margin < 1.0f) << " worst_x " << worst_x << " worst_val " << worst_val << " worst_ref " << worst_ref << " worst_ulp " << worst_ulp;
236*03ce13f7SAndroid Build Coastguard Worker 	ASSERT_TRUE(worst_abs <= 1.0f) << " worst_x " << worst_x << " worst_val " << worst_val << " worst_ref " << worst_ref << " worst_ulp " << worst_ulp;
237*03ce13f7SAndroid Build Coastguard Worker 
238*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setDenormalsAreZero(false);
239*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setFlushToZero(false);
240*03ce13f7SAndroid Build Coastguard Worker }
241*03ce13f7SAndroid Build Coastguard Worker 
242*03ce13f7SAndroid Build Coastguard Worker // lolremez --float -d 2 -r "0:1" "(2^x-x-1)/x" "1/x"
243*03ce13f7SAndroid Build Coastguard Worker // ULP-16: 0.130859017
Pr(float x)244*03ce13f7SAndroid Build Coastguard Worker float Pr(float x)
245*03ce13f7SAndroid Build Coastguard Worker {
246*03ce13f7SAndroid Build Coastguard Worker 	float u = 7.8145574e-2f;
247*03ce13f7SAndroid Build Coastguard Worker 	u = u * x + 2.2617357e-1f;
248*03ce13f7SAndroid Build Coastguard Worker 	return u * x + -3.0444314e-1f;
249*03ce13f7SAndroid Build Coastguard Worker }
250*03ce13f7SAndroid Build Coastguard Worker 
Exp2Relaxed(float x)251*03ce13f7SAndroid Build Coastguard Worker float Exp2Relaxed(float x)
252*03ce13f7SAndroid Build Coastguard Worker {
253*03ce13f7SAndroid Build Coastguard Worker 	x = min(x, 128.0f);
254*03ce13f7SAndroid Build Coastguard Worker 	x = max(x, bit_cast<float>(int(0xC2FDFFFF)));  // -126.999992
255*03ce13f7SAndroid Build Coastguard Worker 
256*03ce13f7SAndroid Build Coastguard Worker 	// 2^f - f - 1 as P(f) * f
257*03ce13f7SAndroid Build Coastguard Worker 	// This is a correction term to be added to 1+x to obtain 2^x.
258*03ce13f7SAndroid Build Coastguard Worker 	float f = x - floor(x);
259*03ce13f7SAndroid Build Coastguard Worker 	float y = Pr(f) * f + x;
260*03ce13f7SAndroid Build Coastguard Worker 
261*03ce13f7SAndroid Build Coastguard Worker 	// bit_cast<float>(int(x * 2^23)) is a piecewise linear approximation of 2^(x-127).
262*03ce13f7SAndroid Build Coastguard Worker 	// See "Fast Exponential Computation on SIMD Architectures" by Malossi et al.
263*03ce13f7SAndroid Build Coastguard Worker 	return bit_cast<float>(int((1 << 23) * y + (127 << 23)));
264*03ce13f7SAndroid Build Coastguard Worker }
265*03ce13f7SAndroid Build Coastguard Worker 
TEST(MathTest,Exp2RelaxedExhaustive)266*03ce13f7SAndroid Build Coastguard Worker TEST(MathTest, Exp2RelaxedExhaustive)
267*03ce13f7SAndroid Build Coastguard Worker {
268*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setDenormalsAreZero(true);
269*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setFlushToZero(true);
270*03ce13f7SAndroid Build Coastguard Worker 
271*03ce13f7SAndroid Build Coastguard Worker 	float worst_margin = 0;
272*03ce13f7SAndroid Build Coastguard Worker 	float worst_ulp = 0;
273*03ce13f7SAndroid Build Coastguard Worker 	float worst_x = 0;
274*03ce13f7SAndroid Build Coastguard Worker 	float worst_val = 0;
275*03ce13f7SAndroid Build Coastguard Worker 	float worst_ref = 0;
276*03ce13f7SAndroid Build Coastguard Worker 
277*03ce13f7SAndroid Build Coastguard Worker 	for(float x = -10; x <= 10; x = inc(x))
278*03ce13f7SAndroid Build Coastguard Worker 	{
279*03ce13f7SAndroid Build Coastguard Worker 		float val = Exp2Relaxed(x);
280*03ce13f7SAndroid Build Coastguard Worker 
281*03ce13f7SAndroid Build Coastguard Worker 		double ref = exp2((double)x);
282*03ce13f7SAndroid Build Coastguard Worker 
283*03ce13f7SAndroid Build Coastguard Worker 		if(x == (int)x)
284*03ce13f7SAndroid Build Coastguard Worker 		{
285*03ce13f7SAndroid Build Coastguard Worker 			ASSERT_EQ(val, ref);
286*03ce13f7SAndroid Build Coastguard Worker 		}
287*03ce13f7SAndroid Build Coastguard Worker 
288*03ce13f7SAndroid Build Coastguard Worker 		const float tolerance = (1 + 2 * abs(x));
289*03ce13f7SAndroid Build Coastguard Worker 		float ulp = ULP_16((float)ref, val);
290*03ce13f7SAndroid Build Coastguard Worker 		float margin = ulp / tolerance;
291*03ce13f7SAndroid Build Coastguard Worker 
292*03ce13f7SAndroid Build Coastguard Worker 		if(margin > worst_margin)
293*03ce13f7SAndroid Build Coastguard Worker 		{
294*03ce13f7SAndroid Build Coastguard Worker 			worst_margin = margin;
295*03ce13f7SAndroid Build Coastguard Worker 			worst_ulp = ulp;
296*03ce13f7SAndroid Build Coastguard Worker 			worst_x = x;
297*03ce13f7SAndroid Build Coastguard Worker 			worst_val = val;
298*03ce13f7SAndroid Build Coastguard Worker 			worst_ref = ref;
299*03ce13f7SAndroid Build Coastguard Worker 		}
300*03ce13f7SAndroid Build Coastguard Worker 	}
301*03ce13f7SAndroid Build Coastguard Worker 
302*03ce13f7SAndroid Build Coastguard Worker 	ASSERT_TRUE(worst_margin <= 1.0f) << " worst_x " << worst_x << " worst_val " << worst_val << " worst_ref " << worst_ref << " worst_ulp " << worst_ulp;
303*03ce13f7SAndroid Build Coastguard Worker 
304*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setDenormalsAreZero(false);
305*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setFlushToZero(false);
306*03ce13f7SAndroid Build Coastguard Worker }
307*03ce13f7SAndroid Build Coastguard Worker 
308*03ce13f7SAndroid Build Coastguard Worker // lolremez --float -d 7 -r "0:1" "(log2(x+1)-x)/x" "1/x"
309*03ce13f7SAndroid Build Coastguard Worker // ULP-32: 1.69571960, abs: 0.360798746
Pl(float x)310*03ce13f7SAndroid Build Coastguard Worker float Pl(float x)
311*03ce13f7SAndroid Build Coastguard Worker {
312*03ce13f7SAndroid Build Coastguard Worker 	float u = -9.3091638e-3f;
313*03ce13f7SAndroid Build Coastguard Worker 	u = u * x + 5.2059003e-2f;
314*03ce13f7SAndroid Build Coastguard Worker 	u = u * x + -1.3752135e-1f;
315*03ce13f7SAndroid Build Coastguard Worker 	u = u * x + 2.4186478e-1f;
316*03ce13f7SAndroid Build Coastguard Worker 	u = u * x + -3.4730109e-1f;
317*03ce13f7SAndroid Build Coastguard Worker 	u = u * x + 4.786837e-1f;
318*03ce13f7SAndroid Build Coastguard Worker 	u = u * x + -7.2116581e-1f;
319*03ce13f7SAndroid Build Coastguard Worker 	return u * x + 4.4268988e-1f;
320*03ce13f7SAndroid Build Coastguard Worker }
321*03ce13f7SAndroid Build Coastguard Worker 
Log2(float x)322*03ce13f7SAndroid Build Coastguard Worker float Log2(float x)
323*03ce13f7SAndroid Build Coastguard Worker {
324*03ce13f7SAndroid Build Coastguard Worker 	// Reinterpretation as an integer provides a piecewise linear
325*03ce13f7SAndroid Build Coastguard Worker 	// approximation of log2(). Scale to the radix and subtract exponent bias.
326*03ce13f7SAndroid Build Coastguard Worker 	int im = bit_cast<int>(x);
327*03ce13f7SAndroid Build Coastguard Worker 	float y = (float)(im - (127 << 23)) * (1.0f / (1 << 23));
328*03ce13f7SAndroid Build Coastguard Worker 
329*03ce13f7SAndroid Build Coastguard Worker 	// Handle log2(inf) = inf.
330*03ce13f7SAndroid Build Coastguard Worker 	if(im == 0x7F800000) y = INFINITY;
331*03ce13f7SAndroid Build Coastguard Worker 
332*03ce13f7SAndroid Build Coastguard Worker 	float m = (float)(im & 0x007FFFFF) * (1.0f / (1 << 23));  // Normalized mantissa of x.
333*03ce13f7SAndroid Build Coastguard Worker 
334*03ce13f7SAndroid Build Coastguard Worker 	// Add a polynomial approximation of log2(m+1)-m to the result's mantissa.
335*03ce13f7SAndroid Build Coastguard Worker 	return Pl(m) * m + y;
336*03ce13f7SAndroid Build Coastguard Worker }
337*03ce13f7SAndroid Build Coastguard Worker 
TEST(MathTest,Log2Exhaustive)338*03ce13f7SAndroid Build Coastguard Worker TEST(MathTest, Log2Exhaustive)
339*03ce13f7SAndroid Build Coastguard Worker {
340*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setDenormalsAreZero(true);
341*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setFlushToZero(true);
342*03ce13f7SAndroid Build Coastguard Worker 
343*03ce13f7SAndroid Build Coastguard Worker 	float worst_margin = 0;
344*03ce13f7SAndroid Build Coastguard Worker 	float worst_ulp = 0;
345*03ce13f7SAndroid Build Coastguard Worker 	float worst_x = 0;
346*03ce13f7SAndroid Build Coastguard Worker 	float worst_val = 0;
347*03ce13f7SAndroid Build Coastguard Worker 	float worst_ref = 0;
348*03ce13f7SAndroid Build Coastguard Worker 
349*03ce13f7SAndroid Build Coastguard Worker 	float worst_abs = 0;
350*03ce13f7SAndroid Build Coastguard Worker 
351*03ce13f7SAndroid Build Coastguard Worker 	for(float x = 0.0f; x <= INFINITY; x = inc(x))
352*03ce13f7SAndroid Build Coastguard Worker 	{
353*03ce13f7SAndroid Build Coastguard Worker 		float val = Log2(x);
354*03ce13f7SAndroid Build Coastguard Worker 
355*03ce13f7SAndroid Build Coastguard Worker 		double ref = log2((double)x);
356*03ce13f7SAndroid Build Coastguard Worker 
357*03ce13f7SAndroid Build Coastguard Worker 		if(ref == (int)ref)
358*03ce13f7SAndroid Build Coastguard Worker 		{
359*03ce13f7SAndroid Build Coastguard Worker 			ASSERT_EQ(val, ref);
360*03ce13f7SAndroid Build Coastguard Worker 		}
361*03ce13f7SAndroid Build Coastguard Worker 		else if(x >= 0.5f && x <= 2.0f)
362*03ce13f7SAndroid Build Coastguard Worker 		{
363*03ce13f7SAndroid Build Coastguard Worker 			const float tolerance = pow(2.0f, -21.0f);  // Absolute
364*03ce13f7SAndroid Build Coastguard Worker 
365*03ce13f7SAndroid Build Coastguard Worker 			float margin = abs(val - ref) / tolerance;
366*03ce13f7SAndroid Build Coastguard Worker 
367*03ce13f7SAndroid Build Coastguard Worker 			if(margin > worst_abs)
368*03ce13f7SAndroid Build Coastguard Worker 			{
369*03ce13f7SAndroid Build Coastguard Worker 				worst_abs = margin;
370*03ce13f7SAndroid Build Coastguard Worker 			}
371*03ce13f7SAndroid Build Coastguard Worker 		}
372*03ce13f7SAndroid Build Coastguard Worker 		else
373*03ce13f7SAndroid Build Coastguard Worker 		{
374*03ce13f7SAndroid Build Coastguard Worker 			const float tolerance = 3;  // ULP
375*03ce13f7SAndroid Build Coastguard Worker 
376*03ce13f7SAndroid Build Coastguard Worker 			float ulp = (float)ULP_32(ref, (double)val);
377*03ce13f7SAndroid Build Coastguard Worker 			float margin = ulp / tolerance;
378*03ce13f7SAndroid Build Coastguard Worker 
379*03ce13f7SAndroid Build Coastguard Worker 			if(margin > worst_margin)
380*03ce13f7SAndroid Build Coastguard Worker 			{
381*03ce13f7SAndroid Build Coastguard Worker 				worst_margin = margin;
382*03ce13f7SAndroid Build Coastguard Worker 				worst_ulp = ulp;
383*03ce13f7SAndroid Build Coastguard Worker 				worst_x = x;
384*03ce13f7SAndroid Build Coastguard Worker 				worst_val = val;
385*03ce13f7SAndroid Build Coastguard Worker 				worst_ref = ref;
386*03ce13f7SAndroid Build Coastguard Worker 			}
387*03ce13f7SAndroid Build Coastguard Worker 		}
388*03ce13f7SAndroid Build Coastguard Worker 	}
389*03ce13f7SAndroid Build Coastguard Worker 
390*03ce13f7SAndroid Build Coastguard Worker 	ASSERT_TRUE(worst_margin < 1.0f) << " worst_x " << worst_x << " worst_val " << worst_val << " worst_ref " << worst_ref << " worst_ulp " << worst_ulp;
391*03ce13f7SAndroid Build Coastguard Worker 	ASSERT_TRUE(worst_abs <= 1.0f) << " worst_x " << worst_x << " worst_val " << worst_val << " worst_ref " << worst_ref << " worst_ulp " << worst_ulp;
392*03ce13f7SAndroid Build Coastguard Worker 
393*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setDenormalsAreZero(false);
394*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setFlushToZero(false);
395*03ce13f7SAndroid Build Coastguard Worker }
396*03ce13f7SAndroid Build Coastguard Worker 
397*03ce13f7SAndroid Build Coastguard Worker // lolremez --float -d 4 -r "0:1" "(2^x-x-1)/x" "1/x"
398*03ce13f7SAndroid Build Coastguard Worker // ULP_32: 2.14694786, Vulkan margin: 0.686957061
P(float x)399*03ce13f7SAndroid Build Coastguard Worker float P(float x)
400*03ce13f7SAndroid Build Coastguard Worker {
401*03ce13f7SAndroid Build Coastguard Worker 	float u = 1.8852974e-3f;
402*03ce13f7SAndroid Build Coastguard Worker 	u = u * x + 8.9733787e-3f;
403*03ce13f7SAndroid Build Coastguard Worker 	u = u * x + 5.5835927e-2f;
404*03ce13f7SAndroid Build Coastguard Worker 	u = u * x + 2.4015281e-1f;
405*03ce13f7SAndroid Build Coastguard Worker 	return u * x + -3.0684753e-1f;
406*03ce13f7SAndroid Build Coastguard Worker }
407*03ce13f7SAndroid Build Coastguard Worker 
Exp2(float x)408*03ce13f7SAndroid Build Coastguard Worker float Exp2(float x)
409*03ce13f7SAndroid Build Coastguard Worker {
410*03ce13f7SAndroid Build Coastguard Worker 	x = min(x, 128.0f);
411*03ce13f7SAndroid Build Coastguard Worker 	x = max(x, bit_cast<float>(0xC2FDFFFF));  // -126.999992
412*03ce13f7SAndroid Build Coastguard Worker 
413*03ce13f7SAndroid Build Coastguard Worker 	// 2^f - f - 1 as P(f) * f
414*03ce13f7SAndroid Build Coastguard Worker 	// This is a correction term to be added to 1+x to obtain 2^x.
415*03ce13f7SAndroid Build Coastguard Worker 	float f = x - floor(x);
416*03ce13f7SAndroid Build Coastguard Worker 	float y = P(f) * f + x;
417*03ce13f7SAndroid Build Coastguard Worker 
418*03ce13f7SAndroid Build Coastguard Worker 	// bit_cast<float>(int(x * 2^23)) is a piecewise linear approximation of 2^(x-127).
419*03ce13f7SAndroid Build Coastguard Worker 	// See "Fast Exponential Computation on SIMD Architectures" by Malossi et al.
420*03ce13f7SAndroid Build Coastguard Worker 	return bit_cast<float>(int(y * (1 << 23)) + (127 << 23));
421*03ce13f7SAndroid Build Coastguard Worker }
422*03ce13f7SAndroid Build Coastguard Worker 
TEST(MathTest,Exp2Exhaustive)423*03ce13f7SAndroid Build Coastguard Worker TEST(MathTest, Exp2Exhaustive)
424*03ce13f7SAndroid Build Coastguard Worker {
425*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setDenormalsAreZero(true);
426*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setFlushToZero(true);
427*03ce13f7SAndroid Build Coastguard Worker 
428*03ce13f7SAndroid Build Coastguard Worker 	float worst_margin = 0;
429*03ce13f7SAndroid Build Coastguard Worker 	float worst_ulp = 0;
430*03ce13f7SAndroid Build Coastguard Worker 	float worst_x = 0;
431*03ce13f7SAndroid Build Coastguard Worker 	float worst_val = 0;
432*03ce13f7SAndroid Build Coastguard Worker 	float worst_ref = 0;
433*03ce13f7SAndroid Build Coastguard Worker 
434*03ce13f7SAndroid Build Coastguard Worker 	for(float x = -10; x <= 10; x = inc(x))
435*03ce13f7SAndroid Build Coastguard Worker 	{
436*03ce13f7SAndroid Build Coastguard Worker 		float val = Exp2(x);
437*03ce13f7SAndroid Build Coastguard Worker 
438*03ce13f7SAndroid Build Coastguard Worker 		double ref = exp2((double)x);
439*03ce13f7SAndroid Build Coastguard Worker 
440*03ce13f7SAndroid Build Coastguard Worker 		if(x == (int)x)
441*03ce13f7SAndroid Build Coastguard Worker 		{
442*03ce13f7SAndroid Build Coastguard Worker 			ASSERT_EQ(val, ref);
443*03ce13f7SAndroid Build Coastguard Worker 		}
444*03ce13f7SAndroid Build Coastguard Worker 
445*03ce13f7SAndroid Build Coastguard Worker 		const float tolerance = (3 + 2 * abs(x));
446*03ce13f7SAndroid Build Coastguard Worker 		float ulp = (float)ULP_32(ref, (double)val);
447*03ce13f7SAndroid Build Coastguard Worker 		float margin = ulp / tolerance;
448*03ce13f7SAndroid Build Coastguard Worker 
449*03ce13f7SAndroid Build Coastguard Worker 		if(margin > worst_margin)
450*03ce13f7SAndroid Build Coastguard Worker 		{
451*03ce13f7SAndroid Build Coastguard Worker 			worst_margin = margin;
452*03ce13f7SAndroid Build Coastguard Worker 			worst_ulp = ulp;
453*03ce13f7SAndroid Build Coastguard Worker 			worst_x = x;
454*03ce13f7SAndroid Build Coastguard Worker 			worst_val = val;
455*03ce13f7SAndroid Build Coastguard Worker 			worst_ref = ref;
456*03ce13f7SAndroid Build Coastguard Worker 		}
457*03ce13f7SAndroid Build Coastguard Worker 	}
458*03ce13f7SAndroid Build Coastguard Worker 
459*03ce13f7SAndroid Build Coastguard Worker 	ASSERT_TRUE(worst_margin <= 1.0f) << " worst_x " << worst_x << " worst_val " << worst_val << " worst_ref " << worst_ref << " worst_ulp " << worst_ulp;
460*03ce13f7SAndroid Build Coastguard Worker 
461*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setDenormalsAreZero(false);
462*03ce13f7SAndroid Build Coastguard Worker 	CPUID::setFlushToZero(false);
463*03ce13f7SAndroid Build Coastguard Worker }
464*03ce13f7SAndroid Build Coastguard Worker 
465*03ce13f7SAndroid Build Coastguard Worker // Polynomial approximation of order 5 for sin(x * 2 * pi) in the range [-1/4, 1/4]
sin5(float x)466*03ce13f7SAndroid Build Coastguard Worker static float sin5(float x)
467*03ce13f7SAndroid Build Coastguard Worker {
468*03ce13f7SAndroid Build Coastguard Worker 	// A * x^5 + B * x^3 + C * x
469*03ce13f7SAndroid Build Coastguard Worker 	// Exact at x = 0, 1/12, 1/6, 1/4, and their negatives, which correspond to x * 2 * pi = 0, pi/6, pi/3, pi/2
470*03ce13f7SAndroid Build Coastguard Worker 	const float A = (36288 - 20736 * sqrt(3)) / 5;
471*03ce13f7SAndroid Build Coastguard Worker 	const float B = 288 * sqrt(3) - 540;
472*03ce13f7SAndroid Build Coastguard Worker 	const float C = (47 - 9 * sqrt(3)) / 5;
473*03ce13f7SAndroid Build Coastguard Worker 
474*03ce13f7SAndroid Build Coastguard Worker 	float x2 = x * x;
475*03ce13f7SAndroid Build Coastguard Worker 
476*03ce13f7SAndroid Build Coastguard Worker 	return ((A * x2 + B) * x2 + C) * x;
477*03ce13f7SAndroid Build Coastguard Worker }
478*03ce13f7SAndroid Build Coastguard Worker 
TEST(MathTest,SinExhaustive)479*03ce13f7SAndroid Build Coastguard Worker TEST(MathTest, SinExhaustive)
480*03ce13f7SAndroid Build Coastguard Worker {
481*03ce13f7SAndroid Build Coastguard Worker 	const float tolerance = powf(2.0f, -12.0f);  // Vulkan requires absolute error <= 2^−11 inside the range [−pi, pi]
482*03ce13f7SAndroid Build Coastguard Worker 	const float pi = 3.1415926535f;
483*03ce13f7SAndroid Build Coastguard Worker 
484*03ce13f7SAndroid Build Coastguard Worker 	for(float x = -pi; x <= pi; x = inc(x))
485*03ce13f7SAndroid Build Coastguard Worker 	{
486*03ce13f7SAndroid Build Coastguard Worker 		// Range reduction and mirroring
487*03ce13f7SAndroid Build Coastguard Worker 		float x_2 = 0.25f - x * (0.5f / pi);
488*03ce13f7SAndroid Build Coastguard Worker 		float z = 0.25f - fabs(x_2 - round(x_2));
489*03ce13f7SAndroid Build Coastguard Worker 
490*03ce13f7SAndroid Build Coastguard Worker 		float val = sin5(z);
491*03ce13f7SAndroid Build Coastguard Worker 
492*03ce13f7SAndroid Build Coastguard Worker 		ASSERT_NEAR(val, sinf(x), tolerance);
493*03ce13f7SAndroid Build Coastguard Worker 	}
494*03ce13f7SAndroid Build Coastguard Worker }
495*03ce13f7SAndroid Build Coastguard Worker 
TEST(MathTest,CosExhaustive)496*03ce13f7SAndroid Build Coastguard Worker TEST(MathTest, CosExhaustive)
497*03ce13f7SAndroid Build Coastguard Worker {
498*03ce13f7SAndroid Build Coastguard Worker 	const float tolerance = powf(2.0f, -12.0f);  // Vulkan requires absolute error <= 2^−11 inside the range [−pi, pi]
499*03ce13f7SAndroid Build Coastguard Worker 	const float pi = 3.1415926535f;
500*03ce13f7SAndroid Build Coastguard Worker 
501*03ce13f7SAndroid Build Coastguard Worker 	for(float x = -pi; x <= pi; x = inc(x))
502*03ce13f7SAndroid Build Coastguard Worker 	{
503*03ce13f7SAndroid Build Coastguard Worker 		// Phase shift, range reduction, and mirroring
504*03ce13f7SAndroid Build Coastguard Worker 		float x_2 = x * (0.5f / pi);
505*03ce13f7SAndroid Build Coastguard Worker 		float z = 0.25f - fabs(x_2 - round(x_2));
506*03ce13f7SAndroid Build Coastguard Worker 
507*03ce13f7SAndroid Build Coastguard Worker 		float val = sin5(z);
508*03ce13f7SAndroid Build Coastguard Worker 
509*03ce13f7SAndroid Build Coastguard Worker 		ASSERT_NEAR(val, cosf(x), tolerance);
510*03ce13f7SAndroid Build Coastguard Worker 	}
511*03ce13f7SAndroid Build Coastguard Worker }
512*03ce13f7SAndroid Build Coastguard Worker 
TEST(MathTest,UnsignedFloat11_10)513*03ce13f7SAndroid Build Coastguard Worker TEST(MathTest, UnsignedFloat11_10)
514*03ce13f7SAndroid Build Coastguard Worker {
515*03ce13f7SAndroid Build Coastguard Worker 	// Test the largest value which causes underflow to 0, and the smallest value
516*03ce13f7SAndroid Build Coastguard Worker 	// which produces a denormalized result.
517*03ce13f7SAndroid Build Coastguard Worker 
518*03ce13f7SAndroid Build Coastguard Worker 	EXPECT_EQ(R11G11B10F::float32ToFloat11(bit_cast<float>(0x3500007F)), 0x0000);
519*03ce13f7SAndroid Build Coastguard Worker 	EXPECT_EQ(R11G11B10F::float32ToFloat11(bit_cast<float>(0x35000080)), 0x0001);
520*03ce13f7SAndroid Build Coastguard Worker 
521*03ce13f7SAndroid Build Coastguard Worker 	EXPECT_EQ(R11G11B10F::float32ToFloat10(bit_cast<float>(0x3580003F)), 0x0000);
522*03ce13f7SAndroid Build Coastguard Worker 	EXPECT_EQ(R11G11B10F::float32ToFloat10(bit_cast<float>(0x35800040)), 0x0001);
523*03ce13f7SAndroid Build Coastguard Worker }
524*03ce13f7SAndroid Build Coastguard Worker 
525*03ce13f7SAndroid Build Coastguard Worker // Clamps to the [0, hi] range. NaN input produces 0, hi must be non-NaN.
clamp0hi(float x,float hi)526*03ce13f7SAndroid Build Coastguard Worker float clamp0hi(float x, float hi)
527*03ce13f7SAndroid Build Coastguard Worker {
528*03ce13f7SAndroid Build Coastguard Worker 	// If x=NaN, x > 0 will compare false and we return 0.
529*03ce13f7SAndroid Build Coastguard Worker 	if(!(x > 0))
530*03ce13f7SAndroid Build Coastguard Worker 	{
531*03ce13f7SAndroid Build Coastguard Worker 		return 0;
532*03ce13f7SAndroid Build Coastguard Worker 	}
533*03ce13f7SAndroid Build Coastguard Worker 
534*03ce13f7SAndroid Build Coastguard Worker 	// x is non-NaN at this point, so std::min() is safe for non-NaN hi.
535*03ce13f7SAndroid Build Coastguard Worker 	return std::min(x, hi);
536*03ce13f7SAndroid Build Coastguard Worker }
537*03ce13f7SAndroid Build Coastguard Worker 
RGB9E5_reference(float r,float g,float b)538*03ce13f7SAndroid Build Coastguard Worker unsigned int RGB9E5_reference(float r, float g, float b)
539*03ce13f7SAndroid Build Coastguard Worker {
540*03ce13f7SAndroid Build Coastguard Worker 	// Vulkan 1.1.117 section 15.2.1 RGB to Shared Exponent Conversion
541*03ce13f7SAndroid Build Coastguard Worker 
542*03ce13f7SAndroid Build Coastguard Worker 	// B is the exponent bias (15)
543*03ce13f7SAndroid Build Coastguard Worker 	constexpr int g_sharedexp_bias = 15;
544*03ce13f7SAndroid Build Coastguard Worker 
545*03ce13f7SAndroid Build Coastguard Worker 	// N is the number of mantissa bits per component (9)
546*03ce13f7SAndroid Build Coastguard Worker 	constexpr int g_sharedexp_mantissabits = 9;
547*03ce13f7SAndroid Build Coastguard Worker 
548*03ce13f7SAndroid Build Coastguard Worker 	// Emax is the maximum allowed biased exponent value (31)
549*03ce13f7SAndroid Build Coastguard Worker 	constexpr int g_sharedexp_maxexponent = 31;
550*03ce13f7SAndroid Build Coastguard Worker 
551*03ce13f7SAndroid Build Coastguard Worker 	constexpr float g_sharedexp_max =
552*03ce13f7SAndroid Build Coastguard Worker 	    ((static_cast<float>(1 << g_sharedexp_mantissabits) - 1) /
553*03ce13f7SAndroid Build Coastguard Worker 	     static_cast<float>(1 << g_sharedexp_mantissabits)) *
554*03ce13f7SAndroid Build Coastguard Worker 	    static_cast<float>(1 << (g_sharedexp_maxexponent - g_sharedexp_bias));
555*03ce13f7SAndroid Build Coastguard Worker 
556*03ce13f7SAndroid Build Coastguard Worker 	const float red_c = clamp0hi(r, g_sharedexp_max);
557*03ce13f7SAndroid Build Coastguard Worker 	const float green_c = clamp0hi(g, g_sharedexp_max);
558*03ce13f7SAndroid Build Coastguard Worker 	const float blue_c = clamp0hi(b, g_sharedexp_max);
559*03ce13f7SAndroid Build Coastguard Worker 
560*03ce13f7SAndroid Build Coastguard Worker 	const float max_c = fmax(fmax(red_c, green_c), blue_c);
561*03ce13f7SAndroid Build Coastguard Worker 	const float exp_p = fmax(-g_sharedexp_bias - 1, floor(log2(max_c))) + 1 + g_sharedexp_bias;
562*03ce13f7SAndroid Build Coastguard Worker 	const int max_s = static_cast<int>(floor((max_c / exp2(exp_p - g_sharedexp_bias - g_sharedexp_mantissabits)) + 0.5f));
563*03ce13f7SAndroid Build Coastguard Worker 	const int exp_s = static_cast<int>((max_s < exp2(g_sharedexp_mantissabits)) ? exp_p : exp_p + 1);
564*03ce13f7SAndroid Build Coastguard Worker 
565*03ce13f7SAndroid Build Coastguard Worker 	unsigned int R = static_cast<unsigned int>(floor((red_c / exp2(exp_s - g_sharedexp_bias - g_sharedexp_mantissabits)) + 0.5f));
566*03ce13f7SAndroid Build Coastguard Worker 	unsigned int G = static_cast<unsigned int>(floor((green_c / exp2(exp_s - g_sharedexp_bias - g_sharedexp_mantissabits)) + 0.5f));
567*03ce13f7SAndroid Build Coastguard Worker 	unsigned int B = static_cast<unsigned int>(floor((blue_c / exp2(exp_s - g_sharedexp_bias - g_sharedexp_mantissabits)) + 0.5f));
568*03ce13f7SAndroid Build Coastguard Worker 	unsigned int E = exp_s;
569*03ce13f7SAndroid Build Coastguard Worker 
570*03ce13f7SAndroid Build Coastguard Worker 	return (E << 27) | (B << 18) | (G << 9) | R;
571*03ce13f7SAndroid Build Coastguard Worker }
572*03ce13f7SAndroid Build Coastguard Worker 
TEST(MathTest,SharedExponentSparse)573*03ce13f7SAndroid Build Coastguard Worker TEST(MathTest, SharedExponentSparse)
574*03ce13f7SAndroid Build Coastguard Worker {
575*03ce13f7SAndroid Build Coastguard Worker 	for(uint64_t i = 0; i < 0x0000000100000000; i += 0x400)
576*03ce13f7SAndroid Build Coastguard Worker 	{
577*03ce13f7SAndroid Build Coastguard Worker 		float f = bit_cast<float>(i);
578*03ce13f7SAndroid Build Coastguard Worker 
579*03ce13f7SAndroid Build Coastguard Worker 		unsigned int ref = RGB9E5_reference(f, 0.0f, 0.0f);
580*03ce13f7SAndroid Build Coastguard Worker 		unsigned int val = RGB9E5(f, 0.0f, 0.0f);
581*03ce13f7SAndroid Build Coastguard Worker 
582*03ce13f7SAndroid Build Coastguard Worker 		EXPECT_EQ(ref, val);
583*03ce13f7SAndroid Build Coastguard Worker 	}
584*03ce13f7SAndroid Build Coastguard Worker }
585*03ce13f7SAndroid Build Coastguard Worker 
TEST(MathTest,SharedExponentRandom)586*03ce13f7SAndroid Build Coastguard Worker TEST(MathTest, SharedExponentRandom)
587*03ce13f7SAndroid Build Coastguard Worker {
588*03ce13f7SAndroid Build Coastguard Worker 	srand(0);
589*03ce13f7SAndroid Build Coastguard Worker 
590*03ce13f7SAndroid Build Coastguard Worker 	unsigned int x = 0;
591*03ce13f7SAndroid Build Coastguard Worker 	unsigned int y = 0;
592*03ce13f7SAndroid Build Coastguard Worker 	unsigned int z = 0;
593*03ce13f7SAndroid Build Coastguard Worker 
594*03ce13f7SAndroid Build Coastguard Worker 	for(int i = 0; i < 10000000; i++)
595*03ce13f7SAndroid Build Coastguard Worker 	{
596*03ce13f7SAndroid Build Coastguard Worker 		float r = bit_cast<float>(x);
597*03ce13f7SAndroid Build Coastguard Worker 		float g = bit_cast<float>(y);
598*03ce13f7SAndroid Build Coastguard Worker 		float b = bit_cast<float>(z);
599*03ce13f7SAndroid Build Coastguard Worker 
600*03ce13f7SAndroid Build Coastguard Worker 		unsigned int ref = RGB9E5_reference(r, g, b);
601*03ce13f7SAndroid Build Coastguard Worker 		unsigned int val = RGB9E5(r, g, b);
602*03ce13f7SAndroid Build Coastguard Worker 
603*03ce13f7SAndroid Build Coastguard Worker 		EXPECT_EQ(ref, val);
604*03ce13f7SAndroid Build Coastguard Worker 
605*03ce13f7SAndroid Build Coastguard Worker 		x += rand();
606*03ce13f7SAndroid Build Coastguard Worker 		y += rand();
607*03ce13f7SAndroid Build Coastguard Worker 		z += rand();
608*03ce13f7SAndroid Build Coastguard Worker 	}
609*03ce13f7SAndroid Build Coastguard Worker }
610*03ce13f7SAndroid Build Coastguard Worker 
TEST(MathTest,SharedExponentExhaustive)611*03ce13f7SAndroid Build Coastguard Worker TEST(MathTest, SharedExponentExhaustive)
612*03ce13f7SAndroid Build Coastguard Worker {
613*03ce13f7SAndroid Build Coastguard Worker 	for(uint64_t i = 0; i < 0x0000000100000000; i += 1)
614*03ce13f7SAndroid Build Coastguard Worker 	{
615*03ce13f7SAndroid Build Coastguard Worker 		float f = bit_cast<float>(i);
616*03ce13f7SAndroid Build Coastguard Worker 
617*03ce13f7SAndroid Build Coastguard Worker 		unsigned int ref = RGB9E5_reference(f, 0.0f, 0.0f);
618*03ce13f7SAndroid Build Coastguard Worker 		unsigned int val = RGB9E5(f, 0.0f, 0.0f);
619*03ce13f7SAndroid Build Coastguard Worker 
620*03ce13f7SAndroid Build Coastguard Worker 		EXPECT_EQ(ref, val);
621*03ce13f7SAndroid Build Coastguard Worker 	}
622*03ce13f7SAndroid Build Coastguard Worker }
623