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 19 #include <math.h> 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 28 template <typename T1> 29 struct data_int_1_t { 30 int expected; 31 T1 input; 32 }; 33 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 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 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 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 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 }; 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 159 return ::testing::AssertionFailure() 160 << "expected (" << std::hexfloat << expected << ") != actual (" << actual << ")"; 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 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 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 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 } 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 } 301