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) : m_oldMode(deGetRoundingMode())
45 {
46 deSetRoundingMode(mode);
47 }
48
ScopedRoundingMode(void)49 ScopedRoundingMode(void) : m_oldMode(deGetRoundingMode())
50 {
51 }
52
~ScopedRoundingMode(void)53 ~ScopedRoundingMode(void)
54 {
55 deSetRoundingMode(m_oldMode);
56 }
57
58 private:
59 ScopedRoundingMode(const ScopedRoundingMode &);
60 ScopedRoundingMode &operator=(const ScopedRoundingMode &);
61
62 const deRoundingMode m_oldMode;
63 };
64
65 class Interval
66 {
67 public:
68 // Empty interval.
Interval(void)69 Interval(void)
70 : m_hasNaN(false)
71 , m_lo(TCU_INFINITY)
72 , m_hi(-TCU_INFINITY)
73 , m_warningLo(-TCU_INFINITY)
74 , m_warningHi(TCU_INFINITY)
75 {
76 }
77
78 // Intentionally not explicit. Conversion from double to Interval is common
79 // and reasonable.
Interval(double val)80 Interval(double val)
81 : m_hasNaN(!!deIsNaN(val))
82 , m_lo(m_hasNaN ? TCU_INFINITY : val)
83 , m_hi(m_hasNaN ? -TCU_INFINITY : val)
84 , m_warningLo(-TCU_INFINITY)
85 , m_warningHi(TCU_INFINITY)
86 {
87 }
88
Interval(bool hasNaN_,double lo_,double hi_)89 Interval(bool hasNaN_, double lo_, double hi_)
90 : m_hasNaN(hasNaN_)
91 , m_lo(lo_)
92 , m_hi(hi_)
93 , m_warningLo(-TCU_INFINITY)
94 , m_warningHi(TCU_INFINITY)
95 {
96 }
97
Interval(bool hasNaN_,double lo_,double hi_,double wlo_,double whi_)98 Interval(bool hasNaN_, double lo_, double hi_, double wlo_, double whi_)
99 : m_hasNaN(hasNaN_)
100 , m_lo(lo_)
101 , m_hi(hi_)
102 , m_warningLo(wlo_)
103 , m_warningHi(whi_)
104 {
105 }
106
Interval(const Interval & a,const Interval & b)107 Interval(const Interval &a, const Interval &b)
108 : m_hasNaN(a.m_hasNaN || b.m_hasNaN)
109 , m_lo(de::min(a.lo(), b.lo()))
110 , m_hi(de::max(a.hi(), b.hi()))
111 , m_warningLo(de::min(a.warningLo(), b.warningLo()))
112 , m_warningHi(de::max(a.warningHi(), b.warningHi()))
113 {
114 }
115
length(void) const116 double length(void) const
117 {
118 return m_hi - m_lo;
119 }
lo(void) const120 double lo(void) const
121 {
122 return m_lo;
123 }
hi(void) const124 double hi(void) const
125 {
126 return m_hi;
127 }
warningLo(void) const128 double warningLo(void) const
129 {
130 return m_warningLo;
131 }
warningHi(void) const132 double warningHi(void) const
133 {
134 return m_warningHi;
135 }
hasNaN(void) const136 bool hasNaN(void) const
137 {
138 return m_hasNaN;
139 }
nan(void) const140 Interval nan(void) const
141 {
142 return m_hasNaN ? TCU_NAN : Interval();
143 }
empty(void) const144 bool empty(void) const
145 {
146 return m_lo > m_hi;
147 }
148
149 // The interval is represented in double, it can extend outside the range of smaller floating-point formats
150 // and get rounded to infinity.
isFinite(double maxValue) const151 bool isFinite(double maxValue) const
152 {
153 return m_lo > -maxValue && m_hi < maxValue;
154 }
isOrdinary(double maxValue) const155 bool isOrdinary(double maxValue) const
156 {
157 return !hasNaN() && !empty() && isFinite(maxValue);
158 }
159
warning(double lo_,double hi_)160 void warning(double lo_, double hi_)
161 {
162 m_warningLo = lo_;
163 m_warningHi = hi_;
164 }
165
operator |(const Interval & other) const166 Interval operator|(const Interval &other) const
167 {
168 return Interval(m_hasNaN || other.m_hasNaN, de::min(m_lo, other.m_lo), de::max(m_hi, other.m_hi),
169 de::min(m_warningLo, other.m_warningLo), de::max(m_warningHi, other.m_warningHi));
170 }
171
operator |=(const Interval & other)172 Interval &operator|=(const Interval &other)
173 {
174 return (*this = *this | other);
175 }
176
operator &(const Interval & other) const177 Interval operator&(const Interval &other) const
178 {
179 return Interval(m_hasNaN && other.m_hasNaN, de::max(m_lo, other.m_lo), de::min(m_hi, other.m_hi),
180 de::max(m_warningLo, other.m_warningLo), de::min(m_warningHi, other.m_warningHi));
181 }
182
operator &=(const Interval & other)183 Interval &operator&=(const Interval &other)
184 {
185 return (*this = *this & other);
186 }
187
contains(const Interval & other) const188 bool contains(const Interval &other) const
189 {
190 return (other.lo() >= lo() && other.hi() <= hi() && (!other.hasNaN() || hasNaN()));
191 }
192
containsWarning(const Interval & other) const193 bool containsWarning(const Interval &other) const
194 {
195 return (other.lo() >= warningLo() && other.hi() <= warningHi() && (!other.hasNaN() || hasNaN()));
196 }
197
intersects(const Interval & other) const198 bool intersects(const Interval &other) const
199 {
200 return ((other.hi() >= lo() && other.lo() <= hi()) || (other.hasNaN() && hasNaN()));
201 }
202
operator -(void) const203 Interval operator-(void) const
204 {
205 return Interval(hasNaN(), -hi(), -lo(), -warningHi(), -warningLo());
206 }
207
unbounded(bool nan=false)208 static Interval unbounded(bool nan = false)
209 {
210 return Interval(nan, -TCU_INFINITY, TCU_INFINITY);
211 }
212
midpoint(void) const213 double midpoint(void) const
214 {
215 const double h = hi();
216 const double l = lo();
217
218 if (h == -l)
219 return 0.0;
220 if (l == -TCU_INFINITY)
221 return -TCU_INFINITY;
222 if (h == TCU_INFINITY)
223 return TCU_INFINITY;
224
225 const bool negativeH = ::std::signbit(h);
226 const bool negativeL = ::std::signbit(l);
227 double ret;
228
229 if (negativeH != negativeL)
230 {
231 // Different signs. Adding both values should be safe.
232 ret = (h + l) * 0.5;
233 }
234 else
235 {
236 // Same sign. Substracting low from high should be safe.
237 ret = l + (h - l) * 0.5;
238 }
239
240 return ret;
241 }
242
operator ==(const Interval & other) const243 bool operator==(const Interval &other) const
244 {
245 return ((m_hasNaN == other.m_hasNaN) &&
246 ((empty() && other.empty()) || (m_lo == other.m_lo && m_hi == other.m_hi)));
247 }
248
249 private:
250 bool m_hasNaN;
251 double m_lo;
252 double m_hi;
253 double m_warningLo;
254 double m_warningHi;
255 } DE_WARN_UNUSED_TYPE;
256
operator +(const Interval & x)257 inline Interval operator+(const Interval &x)
258 {
259 return x;
260 }
261 Interval exp2(const Interval &x);
262 Interval exp(const Interval &x);
263 int sign(const Interval &x);
264 Interval abs(const Interval &x);
265 Interval inverseSqrt(const Interval &x);
266
267 Interval operator+(const Interval &x, const Interval &y);
268 Interval operator-(const Interval &x, const Interval &y);
269 Interval operator*(const Interval &x, const Interval &y);
270 Interval operator/(const Interval &nom, const Interval &den);
271
operator +=(Interval & x,const Interval & y)272 inline Interval &operator+=(Interval &x, const Interval &y)
273 {
274 return (x = x + y);
275 }
operator -=(Interval & x,const Interval & y)276 inline Interval &operator-=(Interval &x, const Interval &y)
277 {
278 return (x = x - y);
279 }
operator *=(Interval & x,const Interval & y)280 inline Interval &operator*=(Interval &x, const Interval &y)
281 {
282 return (x = x * y);
283 }
operator /=(Interval & x,const Interval & y)284 inline Interval &operator/=(Interval &x, const Interval &y)
285 {
286 return (x = x / y);
287 }
288
289 std::ostream &operator<<(std::ostream &os, const Interval &interval);
290
291 #define TCU_SET_INTERVAL_BOUNDS(DST, VAR, SETLOW, SETHIGH) \
292 do \
293 { \
294 DE_FENV_ACCESS_ON \
295 ::tcu::ScopedRoundingMode VAR##_ctx_; \
296 ::tcu::Interval &VAR##_dst_ = (DST); \
297 ::tcu::Interval VAR##_lo_; \
298 ::tcu::Interval VAR##_hi_; \
299 \
300 { \
301 ::tcu::Interval &VAR = VAR##_lo_; \
302 ::deSetRoundingMode(DE_ROUNDINGMODE_TO_NEGATIVE_INF); \
303 SETLOW; \
304 } \
305 { \
306 ::tcu::Interval &VAR = VAR##_hi_; \
307 ::deSetRoundingMode(DE_ROUNDINGMODE_TO_POSITIVE_INF); \
308 SETHIGH; \
309 } \
310 \
311 VAR##_dst_ = VAR##_lo_ | VAR##_hi_; \
312 } while (false)
313
314 #define TCU_SET_INTERVAL(DST, VAR, BODY) TCU_SET_INTERVAL_BOUNDS(DST, VAR, BODY, BODY)
315
316 //! Set the interval DST to the image of BODY on ARG, assuming that BODY on
317 //! ARG is a monotone function. In practice, BODY is evaluated on both the
318 //! upper and lower bound of ARG, and DST is set to the union of these
319 //! results. While evaluating BODY, PARAM is bound to the bound of ARG, and
320 //! the output of BODY should be stored in VAR.
321 #define TCU_INTERVAL_APPLY_MONOTONE1(DST, PARAM, ARG, VAR, BODY) \
322 do \
323 { \
324 const ::tcu::Interval &VAR##_arg_ = (ARG); \
325 ::tcu::Interval &VAR##_dst_ = (DST); \
326 ::tcu::Interval VAR##_lo_; \
327 ::tcu::Interval VAR##_hi_; \
328 if (VAR##_arg_.empty()) \
329 VAR##_dst_ = Interval(); \
330 else \
331 { \
332 { \
333 const double PARAM = VAR##_arg_.lo(); \
334 ::tcu::Interval &VAR = VAR##_lo_; \
335 BODY; \
336 } \
337 { \
338 const double PARAM = VAR##_arg_.hi(); \
339 ::tcu::Interval &VAR = VAR##_hi_; \
340 BODY; \
341 } \
342 VAR##_dst_ = VAR##_lo_ | VAR##_hi_; \
343 } \
344 if (VAR##_arg_.hasNaN()) \
345 VAR##_dst_ |= TCU_NAN; \
346 } while (false)
347
348 #define TCU_INTERVAL_APPLY_MONOTONE2(DST, P0, A0, P1, A1, VAR, BODY) \
349 TCU_INTERVAL_APPLY_MONOTONE1(DST, P0, A0, tmp2_, TCU_INTERVAL_APPLY_MONOTONE1(tmp2_, P1, A1, VAR, BODY))
350
351 #define TCU_INTERVAL_APPLY_MONOTONE3(DST, P0, A0, P1, A1, P2, A2, VAR, BODY) \
352 TCU_INTERVAL_APPLY_MONOTONE1(DST, P0, A0, tmp3_, TCU_INTERVAL_APPLY_MONOTONE2(tmp3_, P1, A1, P2, A2, VAR, BODY))
353
354 typedef double DoubleFunc1(double);
355 typedef double DoubleFunc2(double, double);
356 typedef double DoubleFunc3(double, double, double);
357 typedef Interval DoubleIntervalFunc1(double);
358 typedef Interval DoubleIntervalFunc2(double, double);
359 typedef Interval DoubleIntervalFunc3(double, double, double);
360
361 Interval applyMonotone(DoubleFunc1 &func, const Interval &arg0);
362 Interval applyMonotone(DoubleFunc2 &func, const Interval &arg0, const Interval &arg1);
363 Interval applyMonotone(DoubleIntervalFunc1 &func, const Interval &arg0);
364 Interval applyMonotone(DoubleIntervalFunc2 &func, const Interval &arg0, const Interval &arg1);
365
366 } // namespace tcu
367
368 #endif // _TCUINTERVAL_HPP
369