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