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 33 #define TCU_INFINITY (::std::numeric_limits<float>::infinity()) 34 #define TCU_NAN (::std::numeric_limits<float>::quiet_NaN()) 35 36 namespace tcu 37 { 38 39 // RAII context for temporarily changing the rounding mode 40 class ScopedRoundingMode 41 { 42 public: 43 ScopedRoundingMode (deRoundingMode mode) 44 : m_oldMode (deGetRoundingMode()) { deSetRoundingMode(mode); } 45 46 ScopedRoundingMode (void) : m_oldMode (deGetRoundingMode()) {} 47 48 ~ScopedRoundingMode (void) { deSetRoundingMode(m_oldMode); } 49 50 private: 51 ScopedRoundingMode (const ScopedRoundingMode&); 52 ScopedRoundingMode& operator= (const ScopedRoundingMode&); 53 54 const deRoundingMode m_oldMode; 55 }; 56 57 class Interval 58 { 59 public: 60 // Empty interval. 61 Interval (void) 62 : m_hasNaN (false) 63 , m_lo (TCU_INFINITY) 64 , m_hi (-TCU_INFINITY) 65 , m_warningLo (-TCU_INFINITY) 66 , m_warningHi (TCU_INFINITY) {} 67 68 // Intentionally not explicit. Conversion from double to Interval is common 69 // and reasonable. 70 Interval (double val) 71 : m_hasNaN (!!deIsNaN(val)) 72 , m_lo (m_hasNaN ? TCU_INFINITY : val) 73 , m_hi (m_hasNaN ? -TCU_INFINITY : val) 74 , m_warningLo (-TCU_INFINITY) 75 , m_warningHi (TCU_INFINITY) {} 76 77 78 Interval(bool hasNaN_, double lo_, double hi_) 79 : m_hasNaN(hasNaN_), m_lo(lo_), m_hi(hi_), m_warningLo(-TCU_INFINITY), m_warningHi(TCU_INFINITY) {} 80 81 Interval(bool hasNaN_, double lo_, double hi_, double wlo_, double whi_) 82 : m_hasNaN(hasNaN_), m_lo(lo_), m_hi(hi_), m_warningLo(wlo_), m_warningHi(whi_) {} 83 84 Interval (const Interval& a, const Interval& b) 85 : m_hasNaN (a.m_hasNaN || b.m_hasNaN) 86 , m_lo (de::min(a.lo(), b.lo())) 87 , m_hi (de::max(a.hi(), b.hi())) 88 , m_warningLo (de::min(a.warningLo(), b.warningLo())) 89 , m_warningHi (de::max(a.warningHi(), b.warningHi())) {} 90 91 double length (void) const { return m_hi - m_lo; } 92 double lo (void) const { return m_lo; } 93 double hi (void) const { return m_hi; } 94 double warningLo (void) const { return m_warningLo; } 95 double warningHi (void) const { return m_warningHi; } 96 bool hasNaN (void) const { return m_hasNaN; } 97 Interval nan (void) const { return m_hasNaN ? TCU_NAN : Interval(); } 98 bool empty (void) const { return m_lo > m_hi; } 99 bool isFinite (void) const { return m_lo > -TCU_INFINITY && m_hi < TCU_INFINITY; } 100 bool isOrdinary (void) const { return !hasNaN() && !empty() && isFinite(); } 101 102 void warning (double lo_, double hi_) 103 { 104 m_warningLo = lo_; 105 m_warningHi = hi_; 106 } 107 108 Interval operator| (const Interval& other) const 109 { 110 return Interval(m_hasNaN || other.m_hasNaN, 111 de::min(m_lo, other.m_lo), 112 de::max(m_hi, other.m_hi), 113 de::min(m_warningLo, other.m_warningLo), 114 de::max(m_warningHi, other.m_warningHi)); 115 } 116 117 Interval& operator|= (const Interval& other) 118 { 119 return (*this = *this | other); 120 } 121 122 Interval operator& (const Interval& other) const 123 { 124 return Interval(m_hasNaN && other.m_hasNaN, 125 de::max(m_lo, other.m_lo), 126 de::min(m_hi, other.m_hi), 127 de::max(m_warningLo, other.m_warningLo), 128 de::min(m_warningHi, other.m_warningHi)); 129 } 130 131 Interval& operator&= (const Interval& other) 132 { 133 return (*this = *this & other); 134 } 135 136 bool contains (const Interval& other) const 137 { 138 return (other.lo() >= lo() && other.hi() <= hi() && 139 (!other.hasNaN() || hasNaN())); 140 } 141 142 bool containsWarning(const Interval& other) const 143 { 144 return (other.lo() >= warningLo() && other.hi() <= warningHi() && 145 (!other.hasNaN() || hasNaN())); 146 } 147 148 bool intersects (const Interval& other) const 149 { 150 return ((other.hi() >= lo() && other.lo() <= hi()) || 151 (other.hasNaN() && hasNaN())); 152 } 153 154 Interval operator- (void) const 155 { 156 return Interval(hasNaN(), -hi(), -lo(), -warningHi(), -warningLo()); 157 } 158 159 static Interval unbounded (bool nan = false) 160 { 161 return Interval(nan, -TCU_INFINITY, TCU_INFINITY); 162 } 163 164 double midpoint (void) const 165 { 166 return 0.5 * (hi() + lo()); // returns NaN when not bounded 167 } 168 169 bool operator== (const Interval& other) const 170 { 171 return ((m_hasNaN == other.m_hasNaN) && 172 ((empty() && other.empty()) || 173 (m_lo == other.m_lo && m_hi == other.m_hi))); 174 } 175 176 private: 177 bool m_hasNaN; 178 double m_lo; 179 double m_hi; 180 double m_warningLo; 181 double m_warningHi; 182 } DE_WARN_UNUSED_TYPE; 183 184 inline Interval operator+ (const Interval& x) { return x; } 185 Interval exp2 (const Interval& x); 186 Interval exp (const Interval& x); 187 int sign (const Interval& x); 188 Interval abs (const Interval& x); 189 Interval inverseSqrt (const Interval& x); 190 191 Interval operator+ (const Interval& x, const Interval& y); 192 Interval operator- (const Interval& x, const Interval& y); 193 Interval operator* (const Interval& x, const Interval& y); 194 Interval operator/ (const Interval& nom, const Interval& den); 195 196 inline Interval& operator+= (Interval& x, const Interval& y) { return (x = x + y); } 197 inline Interval& operator-= (Interval& x, const Interval& y) { return (x = x - y); } 198 inline Interval& operator*= (Interval& x, const Interval& y) { return (x = x * y); } 199 inline Interval& operator/= (Interval& x, const Interval& y) { return (x = x / y); } 200 201 std::ostream& operator<< (std::ostream& os, const Interval& interval); 202 203 #define TCU_SET_INTERVAL_BOUNDS(DST, VAR, SETLOW, SETHIGH) do \ 204 { \ 205 ::tcu::ScopedRoundingMode VAR##_ctx_; \ 206 ::tcu::Interval& VAR##_dst_ = (DST); \ 207 ::tcu::Interval VAR##_lo_; \ 208 ::tcu::Interval VAR##_hi_; \ 209 \ 210 { \ 211 ::tcu::Interval& (VAR) = VAR##_lo_; \ 212 ::deSetRoundingMode(DE_ROUNDINGMODE_TO_NEGATIVE_INF); \ 213 SETLOW; \ 214 } \ 215 { \ 216 ::tcu::Interval& (VAR) = VAR##_hi_; \ 217 ::deSetRoundingMode(DE_ROUNDINGMODE_TO_POSITIVE_INF); \ 218 SETHIGH; \ 219 } \ 220 \ 221 VAR##_dst_ = VAR##_lo_ | VAR##_hi_; \ 222 } while (::deGetFalse()) 223 224 #define TCU_SET_INTERVAL(DST, VAR, BODY) \ 225 TCU_SET_INTERVAL_BOUNDS(DST, VAR, BODY, BODY) 226 227 //! Set the interval DST to the image of BODY on ARG, assuming that BODY on 228 //! ARG is a monotone function. In practice, BODY is evaluated on both the 229 //! upper and lower bound of ARG, and DST is set to the union of these 230 //! results. While evaluating BODY, PARAM is bound to the bound of ARG, and 231 //! the output of BODY should be stored in VAR. 232 #define TCU_INTERVAL_APPLY_MONOTONE1(DST, PARAM, ARG, VAR, BODY) do \ 233 { \ 234 const ::tcu::Interval& VAR##_arg_ = (ARG); \ 235 ::tcu::Interval& VAR##_dst_ = (DST); \ 236 ::tcu::Interval VAR##_lo_; \ 237 ::tcu::Interval VAR##_hi_; \ 238 if (VAR##_arg_.empty()) \ 239 VAR##_dst_ = Interval(); \ 240 else \ 241 { \ 242 { \ 243 const double (PARAM) = VAR##_arg_.lo(); \ 244 ::tcu::Interval& (VAR) = VAR##_lo_; \ 245 BODY; \ 246 } \ 247 { \ 248 const double (PARAM) = VAR##_arg_.hi(); \ 249 ::tcu::Interval& (VAR) = VAR##_hi_; \ 250 BODY; \ 251 } \ 252 VAR##_dst_ = VAR##_lo_ | VAR##_hi_; \ 253 } \ 254 if (VAR##_arg_.hasNaN()) \ 255 VAR##_dst_ |= TCU_NAN; \ 256 } while (::deGetFalse()) 257 258 #define TCU_INTERVAL_APPLY_MONOTONE2(DST, P0, A0, P1, A1, VAR, BODY) \ 259 TCU_INTERVAL_APPLY_MONOTONE1( \ 260 DST, P0, A0, tmp2_, \ 261 TCU_INTERVAL_APPLY_MONOTONE1(tmp2_, P1, A1, VAR, BODY)) 262 263 #define TCU_INTERVAL_APPLY_MONOTONE3(DST, P0, A0, P1, A1, P2, A2, VAR, BODY) \ 264 TCU_INTERVAL_APPLY_MONOTONE1( \ 265 DST, P0, A0, tmp3_, \ 266 TCU_INTERVAL_APPLY_MONOTONE2(tmp3_, P1, A1, P2, A2, VAR, BODY)) 267 268 typedef double DoubleFunc1 (double); 269 typedef double DoubleFunc2 (double, double); 270 typedef double DoubleFunc3 (double, double, double); 271 typedef Interval DoubleIntervalFunc1 (double); 272 typedef Interval DoubleIntervalFunc2 (double, double); 273 typedef Interval DoubleIntervalFunc3 (double, double, double); 274 275 Interval applyMonotone (DoubleFunc1& func, 276 const Interval& arg0); 277 Interval applyMonotone (DoubleFunc2& func, 278 const Interval& arg0, 279 const Interval& arg1); 280 Interval applyMonotone (DoubleIntervalFunc1& func, 281 const Interval& arg0); 282 Interval applyMonotone (DoubleIntervalFunc2& func, 283 const Interval& arg0, 284 const Interval& arg1); 285 286 287 } // tcu 288 289 #endif // _TCUINTERVAL_HPP 290