• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #ifndef _TCUINTERVAL_HPP
2 #define _TCUINTERVAL_HPP
3 /*-------------------------------------------------------------------------
4  * drawElements Quality Program Tester Core
5  * ----------------------------------------
6  *
7  * Copyright 2014 The Android Open Source Project
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  *//*!
22  * \file
23  * \brief Interval arithmetic and floating point precisions.
24  *//*--------------------------------------------------------------------*/
25 
26 #include "tcuDefs.hpp"
27 
28 #include "deMath.h"
29 
30 #include <iostream>
31 #include <limits>
32 #include <cmath>
33 
34 #define TCU_INFINITY	(::std::numeric_limits<float>::infinity())
35 #define TCU_NAN			(::std::numeric_limits<float>::quiet_NaN())
36 
37 namespace tcu
38 {
39 
40 // RAII context for temporarily changing the rounding mode
41 class ScopedRoundingMode
42 {
43 public:
ScopedRoundingMode(deRoundingMode mode)44 							ScopedRoundingMode	(deRoundingMode mode)
45 								: m_oldMode (deGetRoundingMode()) { deSetRoundingMode(mode); }
46 
ScopedRoundingMode(void)47 							ScopedRoundingMode	(void) : m_oldMode (deGetRoundingMode()) {}
48 
~ScopedRoundingMode(void)49 							~ScopedRoundingMode	(void)	{ deSetRoundingMode(m_oldMode); }
50 
51 private:
52 							ScopedRoundingMode	(const ScopedRoundingMode&);
53 	ScopedRoundingMode&		operator=			(const ScopedRoundingMode&);
54 
55 	const deRoundingMode	m_oldMode;
56 };
57 
58 class Interval
59 {
60 public:
61 				// Empty interval.
Interval(void)62 				Interval			(void)
63 					: m_hasNaN		(false)
64 					, m_lo			(TCU_INFINITY)
65 					, m_hi			(-TCU_INFINITY)
66 					, m_warningLo	(-TCU_INFINITY)
67 					, m_warningHi	(TCU_INFINITY) {}
68 
69 				// Intentionally not explicit. Conversion from double to Interval is common
70 				// and reasonable.
Interval(double val)71 				Interval			(double val)
72 					: m_hasNaN		(!!deIsNaN(val))
73 					, m_lo			(m_hasNaN ? TCU_INFINITY : val)
74 					, m_hi			(m_hasNaN ? -TCU_INFINITY : val)
75 					, m_warningLo	(-TCU_INFINITY)
76 					, m_warningHi	(TCU_INFINITY) {}
77 
78 
Interval(bool hasNaN_,double lo_,double hi_)79 				Interval(bool hasNaN_, double lo_, double hi_)
80 					: m_hasNaN(hasNaN_), m_lo(lo_), m_hi(hi_), m_warningLo(-TCU_INFINITY), m_warningHi(TCU_INFINITY) {}
81 
Interval(bool hasNaN_,double lo_,double hi_,double wlo_,double whi_)82 				Interval(bool hasNaN_, double lo_, double hi_, double wlo_, double whi_)
83 					: m_hasNaN(hasNaN_), m_lo(lo_), m_hi(hi_), m_warningLo(wlo_), m_warningHi(whi_) {}
84 
Interval(const Interval & a,const Interval & b)85 				Interval		(const Interval& a, const Interval& b)
86 					: m_hasNaN		(a.m_hasNaN || b.m_hasNaN)
87 					, m_lo			(de::min(a.lo(), b.lo()))
88 					, m_hi			(de::max(a.hi(), b.hi()))
89 					, m_warningLo	(de::min(a.warningLo(), b.warningLo()))
90 					, m_warningHi	(de::max(a.warningHi(), b.warningHi())) {}
91 
length(void) const92 	double		length			(void) const { return m_hi - m_lo; }
lo(void) const93 	double		lo				(void) const { return m_lo; }
hi(void) const94 	double		hi				(void) const { return m_hi; }
warningLo(void) const95 	double		warningLo		(void) const { return m_warningLo; }
warningHi(void) const96 	double		warningHi		(void) const { return m_warningHi; }
hasNaN(void) const97 	bool		hasNaN			(void) const { return m_hasNaN; }
nan(void) const98 	Interval	nan				(void) const { return m_hasNaN ? TCU_NAN : Interval(); }
empty(void) const99 	bool		empty			(void) const { return m_lo > m_hi; }
100 
101 	// The interval is represented in double, it can extend outside the range of smaller floating-point formats
102 	// and get rounded to infinity.
isFinite(double maxValue) const103 	bool		isFinite		(double maxValue) const { return m_lo > -maxValue && m_hi < maxValue; }
isOrdinary(double maxValue) const104 	bool		isOrdinary		(double maxValue) const { return !hasNaN() && !empty() && isFinite(maxValue); }
105 
warning(double lo_,double hi_)106 	void		warning			(double lo_, double hi_)
107 	{
108 		m_warningLo = lo_;
109 		m_warningHi = hi_;
110 	}
111 
operator |(const Interval & other) const112 	Interval	operator|		(const Interval& other) const
113 	{
114 		return Interval(m_hasNaN || other.m_hasNaN,
115 						de::min(m_lo, other.m_lo),
116 						de::max(m_hi, other.m_hi),
117 						de::min(m_warningLo, other.m_warningLo),
118 						de::max(m_warningHi, other.m_warningHi));
119 	}
120 
operator |=(const Interval & other)121 	Interval&	operator|=		(const Interval& other)
122 	{
123 		return (*this = *this | other);
124 	}
125 
operator &(const Interval & other) const126 	Interval	operator&		(const Interval& other) const
127 	{
128 		return Interval(m_hasNaN && other.m_hasNaN,
129 						de::max(m_lo, other.m_lo),
130 						de::min(m_hi, other.m_hi),
131 						de::max(m_warningLo, other.m_warningLo),
132 						de::min(m_warningHi, other.m_warningHi));
133 	}
134 
operator &=(const Interval & other)135 	Interval&	operator&=		(const Interval& other)
136 	{
137 		return (*this = *this & other);
138 	}
139 
contains(const Interval & other) const140 	bool		contains		(const Interval& other) const
141 	{
142 		return (other.lo() >= lo() && other.hi() <= hi() &&
143 				(!other.hasNaN() || hasNaN()));
144 	}
145 
containsWarning(const Interval & other) const146 	bool		containsWarning(const Interval& other) const
147 	{
148 		return (other.lo() >= warningLo() && other.hi() <= warningHi() &&
149 			(!other.hasNaN() || hasNaN()));
150 	}
151 
intersects(const Interval & other) const152 	bool		intersects		(const Interval& other) const
153 	{
154 		return ((other.hi() >= lo() && other.lo() <= hi()) ||
155 				(other.hasNaN() && hasNaN()));
156 	}
157 
operator -(void) const158 	Interval	operator-		(void) const
159 	{
160 		return Interval(hasNaN(), -hi(), -lo(), -warningHi(), -warningLo());
161 	}
162 
unbounded(bool nan=false)163 	static Interval	unbounded	(bool nan = false)
164 	{
165 		return Interval(nan, -TCU_INFINITY, TCU_INFINITY);
166 	}
167 
midpoint(void) const168 	double		midpoint		(void) const
169 	{
170 		const double	h = hi();
171 		const double	l = lo();
172 
173 		if (h == -l)
174 			return 0.0;
175 		if (l == -TCU_INFINITY)
176 			return -TCU_INFINITY;
177 		if (h == TCU_INFINITY)
178 			return TCU_INFINITY;
179 
180 		const bool		negativeH = ::std::signbit(h);
181 		const bool		negativeL = ::std::signbit(l);
182 		double			ret;
183 
184 		if (negativeH != negativeL)
185 		{
186 			// Different signs. Adding both values should be safe.
187 			ret = (h + l) * 0.5;
188 		}
189 		else
190 		{
191 			// Same sign. Substracting low from high should be safe.
192 			ret = l + (h - l) * 0.5;
193 		}
194 
195 		return ret;
196 	}
197 
operator ==(const Interval & other) const198 	bool		operator==		(const Interval& other) const
199 	{
200 		return ((m_hasNaN == other.m_hasNaN) &&
201 				((empty() && other.empty()) ||
202 				 (m_lo == other.m_lo && m_hi == other.m_hi)));
203 	}
204 
205 private:
206 	bool		m_hasNaN;
207 	double		m_lo;
208 	double		m_hi;
209 	double		m_warningLo;
210 	double		m_warningHi;
211 } DE_WARN_UNUSED_TYPE;
212 
operator +(const Interval & x)213 inline Interval	operator+	(const Interval& x) { return x; }
214 Interval		exp2		(const Interval& x);
215 Interval		exp			(const Interval& x);
216 int				sign		(const Interval& x);
217 Interval		abs			(const Interval& x);
218 Interval		inverseSqrt	(const Interval& x);
219 
220 Interval		operator+	(const Interval& x,		const Interval& y);
221 Interval		operator-	(const Interval& x,		const Interval& y);
222 Interval		operator*	(const Interval& x,		const Interval& y);
223 Interval		operator/	(const Interval& nom,	const Interval& den);
224 
operator +=(Interval & x,const Interval & y)225 inline Interval& operator+=	(Interval& x,	const Interval& y) { return (x = x + y); }
operator -=(Interval & x,const Interval & y)226 inline Interval& operator-=	(Interval& x,	const Interval& y) { return (x = x - y); }
operator *=(Interval & x,const Interval & y)227 inline Interval& operator*=	(Interval& x,	const Interval& y) { return (x = x * y); }
operator /=(Interval & x,const Interval & y)228 inline Interval& operator/=	(Interval& x,	const Interval& y) { return (x = x / y); }
229 
230 std::ostream&	operator<<	(std::ostream& os, const Interval& interval);
231 
232 #define TCU_SET_INTERVAL_BOUNDS(DST, VAR, SETLOW, SETHIGH) do	\
233 {																\
234 	::tcu::ScopedRoundingMode	VAR##_ctx_;						\
235 	::tcu::Interval&			VAR##_dst_	= (DST);			\
236 	::tcu::Interval				VAR##_lo_;						\
237 	::tcu::Interval				VAR##_hi_;						\
238 																\
239 	{															\
240 		::tcu::Interval&	VAR = VAR##_lo_;					\
241 		::deSetRoundingMode(DE_ROUNDINGMODE_TO_NEGATIVE_INF);	\
242 		SETLOW;													\
243 	}															\
244 	{															\
245 		::tcu::Interval&	VAR = VAR##_hi_;					\
246 		::deSetRoundingMode(DE_ROUNDINGMODE_TO_POSITIVE_INF);	\
247 		SETHIGH;												\
248 	}															\
249 																\
250 	VAR##_dst_ = VAR##_lo_ | VAR##_hi_;							\
251 } while (::deGetFalse())
252 
253 #define TCU_SET_INTERVAL(DST, VAR, BODY)						\
254 	TCU_SET_INTERVAL_BOUNDS(DST, VAR, BODY, BODY)
255 
256 //! Set the interval DST to the image of BODY on ARG, assuming that BODY on
257 //! ARG is a monotone function. In practice, BODY is evaluated on both the
258 //! upper and lower bound of ARG, and DST is set to the union of these
259 //! results. While evaluating BODY, PARAM is bound to the bound of ARG, and
260 //! the output of BODY should be stored in VAR.
261 #define TCU_INTERVAL_APPLY_MONOTONE1(DST, PARAM, ARG, VAR, BODY) do		\
262 	{																	\
263 	const ::tcu::Interval&	VAR##_arg_		= (ARG);					\
264 	::tcu::Interval&		VAR##_dst_		= (DST);					\
265 	::tcu::Interval			VAR##_lo_;									\
266 	::tcu::Interval			VAR##_hi_;									\
267 	if (VAR##_arg_.empty())												\
268 		VAR##_dst_ = Interval();										\
269 	else																\
270 	{																	\
271 		{																\
272 			const double		PARAM	= VAR##_arg_.lo();				\
273 			::tcu::Interval&	VAR		= VAR##_lo_;					\
274 			BODY;														\
275 		}																\
276 		{																\
277 			const double		PARAM	= VAR##_arg_.hi();				\
278 			::tcu::Interval&	VAR		= VAR##_hi_;					\
279 			BODY;														\
280 		}																\
281 		VAR##_dst_ = VAR##_lo_ | VAR##_hi_;								\
282 	}																	\
283 	if (VAR##_arg_.hasNaN())											\
284 		VAR##_dst_ |= TCU_NAN;											\
285 } while (::deGetFalse())
286 
287 #define TCU_INTERVAL_APPLY_MONOTONE2(DST, P0, A0, P1, A1, VAR, BODY)	\
288 	TCU_INTERVAL_APPLY_MONOTONE1(										\
289 		DST, P0, A0, tmp2_,												\
290 		TCU_INTERVAL_APPLY_MONOTONE1(tmp2_, P1, A1, VAR, BODY))
291 
292 #define TCU_INTERVAL_APPLY_MONOTONE3(DST, P0, A0, P1, A1, P2, A2, VAR, BODY) \
293 	TCU_INTERVAL_APPLY_MONOTONE1(										\
294 		DST, P0, A0, tmp3_,												\
295 		TCU_INTERVAL_APPLY_MONOTONE2(tmp3_, P1, A1, P2, A2, VAR, BODY))
296 
297 typedef double		DoubleFunc1			(double);
298 typedef double		DoubleFunc2			(double, double);
299 typedef double		DoubleFunc3			(double, double, double);
300 typedef Interval	DoubleIntervalFunc1	(double);
301 typedef Interval	DoubleIntervalFunc2	(double, double);
302 typedef Interval	DoubleIntervalFunc3	(double, double, double);
303 
304 Interval	applyMonotone	(DoubleFunc1&			func,
305 							 const Interval&		arg0);
306 Interval	applyMonotone	(DoubleFunc2&			func,
307 							 const Interval&		arg0,
308 							 const Interval&		arg1);
309 Interval	applyMonotone	(DoubleIntervalFunc1&	func,
310 							 const Interval&		arg0);
311 Interval	applyMonotone	(DoubleIntervalFunc2&	func,
312 							 const Interval&		arg0,
313 							 const Interval&		arg1);
314 
315 
316 } // tcu
317 
318 #endif // _TCUINTERVAL_HPP
319