| Elliott Hughes | 1b37ba2 | 2014-11-03 17:03:20 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2014 The Android Open Source Project | 
|  | 3 | * | 
|  | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 5 | * you may not use this file except in compliance with the License. | 
|  | 6 | * You may obtain a copy of the License at | 
|  | 7 | * | 
|  | 8 | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 9 | * | 
|  | 10 | * Unless required by applicable law or agreed to in writing, software | 
|  | 11 | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 13 | * See the License for the specific language governing permissions and | 
|  | 14 | * limitations under the License. | 
|  | 15 | */ | 
|  | 16 |  | 
|  | 17 | #include <gtest/gtest.h> | 
|  | 18 |  | 
| Elliott Hughes | 7f0849f | 2016-08-26 16:17:17 -0700 | [diff] [blame] | 19 | #include <math.h> | 
| Elliott Hughes | 1b37ba2 | 2014-11-03 17:03:20 -0800 | [diff] [blame] | 20 | #include <fenv.h> | 
|  | 21 |  | 
|  | 22 | template <typename RT, typename T1> | 
|  | 23 | struct data_1_1_t { | 
|  | 24 | RT expected; | 
|  | 25 | T1 input; | 
|  | 26 | }; | 
|  | 27 |  | 
| Jingwei Zhang | 56b2b29 | 2014-09-02 21:39:14 +0800 | [diff] [blame] | 28 | template <typename T1> | 
|  | 29 | struct data_int_1_t { | 
|  | 30 | int expected; | 
|  | 31 | T1 input; | 
|  | 32 | }; | 
|  | 33 |  | 
| jzha136 | f3ea093 | 2015-06-12 09:15:02 -0700 | [diff] [blame] | 34 | template <typename T1> | 
|  | 35 | struct data_long_1_t { | 
|  | 36 | long expected; | 
|  | 37 | T1 input; | 
|  | 38 | }; | 
|  | 39 |  | 
|  | 40 | template <typename T1> | 
|  | 41 | struct data_llong_1_t { | 
|  | 42 | long long expected; | 
|  | 43 | T1 input; | 
|  | 44 | }; | 
|  | 45 |  | 
| Elliott Hughes | 1b37ba2 | 2014-11-03 17:03:20 -0800 | [diff] [blame] | 46 | template <typename RT, typename T1, typename T2> | 
|  | 47 | struct data_1_2_t { | 
|  | 48 | RT expected; | 
|  | 49 | T1 input1; | 
|  | 50 | T2 input2; | 
|  | 51 | }; | 
|  | 52 |  | 
|  | 53 | template <typename RT1, typename RT2, typename T> | 
|  | 54 | struct data_2_1_t { | 
|  | 55 | RT1 expected1; | 
|  | 56 | RT2 expected2; | 
|  | 57 | T input; | 
|  | 58 | }; | 
|  | 59 |  | 
| Jingwei Zhang | 56b2b29 | 2014-09-02 21:39:14 +0800 | [diff] [blame] | 60 | template <typename RT1, typename T> | 
|  | 61 | struct data_1_int_1_t { | 
|  | 62 | RT1 expected1; | 
|  | 63 | int expected2; | 
|  | 64 | T input; | 
|  | 65 | }; | 
|  | 66 |  | 
|  | 67 | template <typename RT1, typename T1, typename T2> | 
|  | 68 | struct data_1_int_2_t { | 
|  | 69 | RT1 expected1; | 
|  | 70 | int expected2; | 
|  | 71 | T1 input1; | 
|  | 72 | T2 input2; | 
|  | 73 | }; | 
|  | 74 |  | 
|  | 75 | template <typename RT, typename T1, typename T2, typename T3> | 
|  | 76 | struct data_1_3_t { | 
|  | 77 | RT expected; | 
|  | 78 | T1 input1; | 
|  | 79 | T2 input2; | 
|  | 80 | T3 input3; | 
|  | 81 | }; | 
|  | 82 |  | 
| Elliott Hughes | 1b37ba2 | 2014-11-03 17:03:20 -0800 | [diff] [blame] | 83 | template <typename T> union fp_u; | 
|  | 84 |  | 
|  | 85 | template <> union fp_u<float> { | 
|  | 86 | float value; | 
|  | 87 | struct { | 
|  | 88 | unsigned frac:23; | 
|  | 89 | unsigned exp:8; | 
|  | 90 | unsigned sign:1; | 
|  | 91 | } bits; | 
|  | 92 | uint32_t sign_magnitude; | 
|  | 93 | }; | 
|  | 94 |  | 
|  | 95 | template <> union fp_u<double> { | 
|  | 96 | double value; | 
|  | 97 | struct { | 
|  | 98 | unsigned fracl; | 
|  | 99 | unsigned frach:20; | 
|  | 100 | unsigned exp:11; | 
|  | 101 | unsigned sign:1; | 
|  | 102 | } bits; | 
|  | 103 | uint64_t sign_magnitude; | 
|  | 104 | }; | 
|  | 105 |  | 
| Elliott Hughes | 7f0849f | 2016-08-26 16:17:17 -0700 | [diff] [blame] | 106 | template <> union fp_u<long double> { | 
|  | 107 | long double value; | 
|  | 108 | #if defined(__LP64__) | 
|  | 109 | struct { | 
|  | 110 | unsigned fracl; | 
|  | 111 | unsigned fraclm; | 
|  | 112 | unsigned frachm; | 
|  | 113 | unsigned frach:16; | 
|  | 114 | unsigned exp:15; | 
|  | 115 | unsigned sign:1; | 
|  | 116 | } bits; | 
|  | 117 | __int128_t sign_magnitude; | 
|  | 118 | #else | 
|  | 119 | struct { | 
|  | 120 | unsigned fracl; | 
|  | 121 | unsigned frach:20; | 
|  | 122 | unsigned exp:11; | 
|  | 123 | unsigned sign:1; | 
|  | 124 | } bits; | 
|  | 125 | uint64_t sign_magnitude; | 
|  | 126 | #endif | 
|  | 127 | }; | 
| Elliott Hughes | 1b37ba2 | 2014-11-03 17:03:20 -0800 | [diff] [blame] | 128 |  | 
|  | 129 | template <typename T> | 
|  | 130 | static inline auto SignAndMagnitudeToBiased(const T& value) -> decltype(fp_u<T>::sign_magnitude) { | 
|  | 131 | fp_u<T> u; | 
|  | 132 | u.value = value; | 
|  | 133 | if (u.bits.sign) { | 
|  | 134 | return ~u.sign_magnitude + 1; | 
|  | 135 | } else { | 
|  | 136 | u.bits.sign = 1; | 
|  | 137 | return u.sign_magnitude; | 
|  | 138 | } | 
|  | 139 | } | 
|  | 140 |  | 
|  | 141 | // Based on the existing googletest implementation, which uses a fixed 4 ulp bound. | 
|  | 142 | template <typename T> | 
|  | 143 | size_t UlpDistance(T lhs, T rhs) { | 
|  | 144 | const auto biased1 = SignAndMagnitudeToBiased(lhs); | 
|  | 145 | const auto biased2 = SignAndMagnitudeToBiased(rhs); | 
|  | 146 | return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); | 
|  | 147 | } | 
|  | 148 |  | 
|  | 149 | template <size_t ULP, typename T> | 
|  | 150 | struct FpUlpEq { | 
|  | 151 | ::testing::AssertionResult operator()(const char* /* expected_expression */, | 
|  | 152 | const char* /* actual_expression */, | 
|  | 153 | T expected, | 
|  | 154 | T actual) { | 
|  | 155 | if (!isnan(expected) && !isnan(actual) && UlpDistance(expected, actual) <= ULP) { | 
|  | 156 | return ::testing::AssertionSuccess(); | 
|  | 157 | } | 
|  | 158 |  | 
| Elliott Hughes | 1b37ba2 | 2014-11-03 17:03:20 -0800 | [diff] [blame] | 159 | return ::testing::AssertionFailure() | 
| Elliott Hughes | 7f0849f | 2016-08-26 16:17:17 -0700 | [diff] [blame] | 160 | << "expected (" << std::hexfloat << expected << ") != actual (" << actual << ")"; | 
| Elliott Hughes | 1b37ba2 | 2014-11-03 17:03:20 -0800 | [diff] [blame] | 161 | } | 
|  | 162 | }; | 
|  | 163 |  | 
|  | 164 | // Runs through the array 'data' applying 'f' to each of the input values | 
|  | 165 | // and asserting that the result is within ULP ulps of the expected value. | 
|  | 166 | // For testing a (double) -> double function like sin(3). | 
|  | 167 | template <size_t ULP, typename RT, typename T, size_t N> | 
|  | 168 | void DoMathDataTest(data_1_1_t<RT, T> (&data)[N], RT f(T)) { | 
|  | 169 | fesetenv(FE_DFL_ENV); | 
|  | 170 | FpUlpEq<ULP, RT> predicate; | 
|  | 171 | for (size_t i = 0; i < N; ++i) { | 
|  | 172 | EXPECT_PRED_FORMAT2(predicate, | 
|  | 173 | data[i].expected, f(data[i].input)) << "Failed on element " << i; | 
|  | 174 | } | 
|  | 175 | } | 
|  | 176 |  | 
| Jingwei Zhang | 56b2b29 | 2014-09-02 21:39:14 +0800 | [diff] [blame] | 177 | // Runs through the array 'data' applying 'f' to each of the input values | 
|  | 178 | // and asserting that the result is within ULP ulps of the expected value. | 
|  | 179 | // For testing a (double) -> int function like ilogb(3). | 
|  | 180 | template <size_t ULP, typename T, size_t N> | 
|  | 181 | void DoMathDataTest(data_int_1_t<T> (&data)[N], int f(T)) { | 
|  | 182 | fesetenv(FE_DFL_ENV); | 
|  | 183 | for (size_t i = 0; i < N; ++i) { | 
|  | 184 | EXPECT_EQ(data[i].expected, f(data[i].input)) << "Failed on element " << i; | 
|  | 185 | } | 
|  | 186 | } | 
|  | 187 |  | 
| jzha136 | f3ea093 | 2015-06-12 09:15:02 -0700 | [diff] [blame] | 188 | // Runs through the array 'data' applying 'f' to each of the input values | 
|  | 189 | // and asserting that the result is within ULP ulps of the expected value. | 
|  | 190 | // For testing a (double) -> long int function like lrint(3). | 
|  | 191 | template <size_t ULP, typename T, size_t N> | 
|  | 192 | void DoMathDataTest(data_long_1_t<T> (&data)[N], long f(T)) { | 
|  | 193 | fesetenv(FE_DFL_ENV); | 
|  | 194 | for (size_t i = 0; i < N; ++i) { | 
|  | 195 | EXPECT_EQ(data[i].expected, f(data[i].input)) << "Failed on element " << i; | 
|  | 196 | } | 
|  | 197 | } | 
|  | 198 |  | 
|  | 199 | // Runs through the array 'data' applying 'f' to each of the input values | 
|  | 200 | // and asserting that the result is within ULP ulps of the expected value. | 
|  | 201 | // For testing a (double) -> long long int function like llrint(3). | 
|  | 202 | template <size_t ULP, typename T, size_t N> | 
|  | 203 | void DoMathDataTest(data_llong_1_t<T> (&data)[N], long long f(T)) { | 
|  | 204 | fesetenv(FE_DFL_ENV); | 
|  | 205 | for (size_t i = 0; i < N; ++i) { | 
|  | 206 | EXPECT_EQ(data[i].expected, f(data[i].input)) << "Failed on element " << i; | 
|  | 207 | } | 
|  | 208 | } | 
|  | 209 |  | 
| Elliott Hughes | 1b37ba2 | 2014-11-03 17:03:20 -0800 | [diff] [blame] | 210 | // Runs through the array 'data' applying 'f' to each of the pairs of input values | 
|  | 211 | // and asserting that the result is within ULP ulps of the expected value. | 
|  | 212 | // For testing a (double, double) -> double function like pow(3). | 
|  | 213 | template <size_t ULP, typename RT, typename T1, typename T2, size_t N> | 
|  | 214 | void DoMathDataTest(data_1_2_t<RT, T1, T2> (&data)[N], RT f(T1, T2)) { | 
|  | 215 | fesetenv(FE_DFL_ENV); | 
|  | 216 | FpUlpEq<ULP, RT> predicate; | 
|  | 217 | for (size_t i = 0; i < N; ++i) { | 
|  | 218 | EXPECT_PRED_FORMAT2(predicate, | 
|  | 219 | data[i].expected, f(data[i].input1, data[i].input2)) << "Failed on element " << i; | 
|  | 220 | } | 
|  | 221 | } | 
|  | 222 |  | 
|  | 223 | // Runs through the array 'data' applying 'f' to each of the input values | 
|  | 224 | // and asserting that the results are within ULP ulps of the expected values. | 
|  | 225 | // For testing a (double, double*, double*) -> void function like sincos(3). | 
|  | 226 | template <size_t ULP, typename RT1, typename RT2, typename T1, size_t N> | 
|  | 227 | void DoMathDataTest(data_2_1_t<RT1, RT2, T1> (&data)[N], void f(T1, RT1*, RT2*)) { | 
|  | 228 | fesetenv(FE_DFL_ENV); | 
|  | 229 | FpUlpEq<ULP, RT1> predicate1; | 
|  | 230 | FpUlpEq<ULP, RT2> predicate2; | 
|  | 231 | for (size_t i = 0; i < N; ++i) { | 
|  | 232 | RT1 out1; | 
|  | 233 | RT2 out2; | 
|  | 234 | f(data[i].input, &out1, &out2); | 
|  | 235 | EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i; | 
|  | 236 | EXPECT_PRED_FORMAT2(predicate2, data[i].expected2, out2) << "Failed on element " << i; | 
|  | 237 | } | 
|  | 238 | } | 
| Jingwei Zhang | 56b2b29 | 2014-09-02 21:39:14 +0800 | [diff] [blame] | 239 |  | 
|  | 240 | // Runs through the array 'data' applying 'f' to each of the input values | 
|  | 241 | // and asserting that the results are within ULP ulps of the expected values. | 
|  | 242 | // For testing a (double, double*) -> double function like modf(3). | 
|  | 243 | template <size_t ULP, typename RT1, typename RT2, typename T1, size_t N> | 
|  | 244 | void DoMathDataTest(data_2_1_t<RT1, RT2, T1> (&data)[N], RT1 f(T1, RT2*)) { | 
|  | 245 | fesetenv(FE_DFL_ENV); | 
|  | 246 | FpUlpEq<ULP, RT1> predicate1; | 
|  | 247 | FpUlpEq<ULP, RT2> predicate2; | 
|  | 248 | for (size_t i = 0; i < N; ++i) { | 
|  | 249 | RT1 out1; | 
|  | 250 | RT2 out2; | 
|  | 251 | out1 = f(data[i].input, &out2); | 
|  | 252 | EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i; | 
|  | 253 | EXPECT_PRED_FORMAT2(predicate2, data[i].expected2, out2) << "Failed on element " << i; | 
|  | 254 | } | 
|  | 255 | } | 
|  | 256 |  | 
|  | 257 | // Runs through the array 'data' applying 'f' to each of the input values | 
|  | 258 | // and asserting that the results are within ULP ulps of the expected values. | 
|  | 259 | // For testing a (double, int*) -> double function like frexp(3). | 
|  | 260 | template <size_t ULP, typename RT1, typename T1, size_t N> | 
|  | 261 | void DoMathDataTest(data_1_int_1_t<RT1, T1> (&data)[N], RT1 f(T1, int*)) { | 
|  | 262 | fesetenv(FE_DFL_ENV); | 
|  | 263 | FpUlpEq<ULP, RT1> predicate1; | 
|  | 264 | for (size_t i = 0; i < N; ++i) { | 
|  | 265 | RT1 out1; | 
|  | 266 | int out2; | 
|  | 267 | out1 = f(data[i].input, &out2); | 
|  | 268 | EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i; | 
|  | 269 | EXPECT_EQ(data[i].expected2, out2) << "Failed on element " << i; | 
|  | 270 | } | 
|  | 271 | } | 
|  | 272 |  | 
|  | 273 | // Runs through the array 'data' applying 'f' to each of the input values | 
|  | 274 | // and asserting that the results are within ULP ulps of the expected values. | 
|  | 275 | // For testing a (double, double, int*) -> double function like remquo(3). | 
|  | 276 | template <size_t ULP, typename RT1, typename T1, typename T2, size_t N> | 
|  | 277 | void DoMathDataTest(data_1_int_2_t<RT1, T1, T2> (&data)[N], RT1 f(T1, T2, int*)) { | 
|  | 278 | fesetenv(FE_DFL_ENV); | 
|  | 279 | FpUlpEq<ULP, RT1> predicate1; | 
|  | 280 | for (size_t i = 0; i < N; ++i) { | 
|  | 281 | RT1 out1; | 
|  | 282 | int out2; | 
|  | 283 | out1 = f(data[i].input1, data[i].input2, &out2); | 
|  | 284 | EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i; | 
|  | 285 | EXPECT_EQ(data[i].expected2, out2) << "Failed on element " << i; | 
|  | 286 | } | 
|  | 287 | } | 
|  | 288 |  | 
|  | 289 | // Runs through the array 'data' applying 'f' to each of the pairs of input values | 
|  | 290 | // and asserting that the result is within ULP ulps of the expected value. | 
|  | 291 | // For testing a (double, double, double) -> double function like fma(3). | 
|  | 292 | template <size_t ULP, typename RT, typename T1, typename T2, typename T3, size_t N> | 
|  | 293 | void DoMathDataTest(data_1_3_t<RT, T1, T2, T3> (&data)[N], RT f(T1, T2, T3)) { | 
|  | 294 | fesetenv(FE_DFL_ENV); | 
|  | 295 | FpUlpEq<ULP, RT> predicate; | 
|  | 296 | for (size_t i = 0; i < N; ++i) { | 
|  | 297 | EXPECT_PRED_FORMAT2(predicate, | 
|  | 298 | data[i].expected, f(data[i].input1, data[i].input2, data[i].input3)) << "Failed on element " << i; | 
|  | 299 | } | 
|  | 300 | } |