xref: /aosp_15_r20/external/deqp/framework/common/tcuInterval.hpp (revision 35238bce31c2a825756842865a792f8cf7f89930)
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