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 <fenv.h> 20 21 template <typename RT, typename T1> 22 struct data_1_1_t { 23 RT expected; 24 T1 input; 25 }; 26 27 template <typename T1> 28 struct data_int_1_t { 29 int expected; 30 T1 input; 31 }; 32 33 template <typename T1> 34 struct data_long_1_t { 35 long expected; 36 T1 input; 37 }; 38 39 template <typename T1> 40 struct data_llong_1_t { 41 long long expected; 42 T1 input; 43 }; 44 45 template <typename RT, typename T1, typename T2> 46 struct data_1_2_t { 47 RT expected; 48 T1 input1; 49 T2 input2; 50 }; 51 52 template <typename RT1, typename RT2, typename T> 53 struct data_2_1_t { 54 RT1 expected1; 55 RT2 expected2; 56 T input; 57 }; 58 59 template <typename RT1, typename T> 60 struct data_1_int_1_t { 61 RT1 expected1; 62 int expected2; 63 T input; 64 }; 65 66 template <typename RT1, typename T1, typename T2> 67 struct data_1_int_2_t { 68 RT1 expected1; 69 int expected2; 70 T1 input1; 71 T2 input2; 72 }; 73 74 template <typename RT, typename T1, typename T2, typename T3> 75 struct data_1_3_t { 76 RT expected; 77 T1 input1; 78 T2 input2; 79 T3 input3; 80 }; 81 82 template <typename T> union fp_u; 83 84 template <> union fp_u<float> { 85 float value; 86 struct { 87 unsigned frac:23; 88 unsigned exp:8; 89 unsigned sign:1; 90 } bits; 91 uint32_t sign_magnitude; 92 }; 93 94 template <> union fp_u<double> { 95 double value; 96 struct { 97 unsigned fracl; 98 unsigned frach:20; 99 unsigned exp:11; 100 unsigned sign:1; 101 } bits; 102 uint64_t sign_magnitude; 103 }; 104 105 // TODO: long double. 106 107 template <typename T> 108 static inline auto SignAndMagnitudeToBiased(const T& value) -> decltype(fp_u<T>::sign_magnitude) { 109 fp_u<T> u; 110 u.value = value; 111 if (u.bits.sign) { 112 return ~u.sign_magnitude + 1; 113 } else { 114 u.bits.sign = 1; 115 return u.sign_magnitude; 116 } 117 } 118 119 // Based on the existing googletest implementation, which uses a fixed 4 ulp bound. 120 template <typename T> 121 size_t UlpDistance(T lhs, T rhs) { 122 const auto biased1 = SignAndMagnitudeToBiased(lhs); 123 const auto biased2 = SignAndMagnitudeToBiased(rhs); 124 return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); 125 } 126 127 template <size_t ULP, typename T> 128 struct FpUlpEq { 129 ::testing::AssertionResult operator()(const char* /* expected_expression */, 130 const char* /* actual_expression */, 131 T expected, 132 T actual) { 133 if (!isnan(expected) && !isnan(actual) && UlpDistance(expected, actual) <= ULP) { 134 return ::testing::AssertionSuccess(); 135 } 136 137 // Output the actual and expected values as hex floating point. 138 char expected_str[64]; 139 char actual_str[64]; 140 snprintf(expected_str, sizeof(expected_str), "%a", expected); 141 snprintf(actual_str, sizeof(actual_str), "%a", actual); 142 143 return ::testing::AssertionFailure() 144 << "expected (" << expected_str << ") != actual (" << actual_str << ")"; 145 } 146 }; 147 148 // Runs through the array 'data' applying 'f' to each of the input values 149 // and asserting that the result is within ULP ulps of the expected value. 150 // For testing a (double) -> double function like sin(3). 151 template <size_t ULP, typename RT, typename T, size_t N> 152 void DoMathDataTest(data_1_1_t<RT, T> (&data)[N], RT f(T)) { 153 fesetenv(FE_DFL_ENV); 154 FpUlpEq<ULP, RT> predicate; 155 for (size_t i = 0; i < N; ++i) { 156 EXPECT_PRED_FORMAT2(predicate, 157 data[i].expected, f(data[i].input)) << "Failed on element " << i; 158 } 159 } 160 161 // Runs through the array 'data' applying 'f' to each of the input values 162 // and asserting that the result is within ULP ulps of the expected value. 163 // For testing a (double) -> int function like ilogb(3). 164 template <size_t ULP, typename T, size_t N> 165 void DoMathDataTest(data_int_1_t<T> (&data)[N], int f(T)) { 166 fesetenv(FE_DFL_ENV); 167 for (size_t i = 0; i < N; ++i) { 168 EXPECT_EQ(data[i].expected, f(data[i].input)) << "Failed on element " << i; 169 } 170 } 171 172 // Runs through the array 'data' applying 'f' to each of the input values 173 // and asserting that the result is within ULP ulps of the expected value. 174 // For testing a (double) -> long int function like lrint(3). 175 template <size_t ULP, typename T, size_t N> 176 void DoMathDataTest(data_long_1_t<T> (&data)[N], long f(T)) { 177 fesetenv(FE_DFL_ENV); 178 for (size_t i = 0; i < N; ++i) { 179 EXPECT_EQ(data[i].expected, f(data[i].input)) << "Failed on element " << i; 180 } 181 } 182 183 // Runs through the array 'data' applying 'f' to each of the input values 184 // and asserting that the result is within ULP ulps of the expected value. 185 // For testing a (double) -> long long int function like llrint(3). 186 template <size_t ULP, typename T, size_t N> 187 void DoMathDataTest(data_llong_1_t<T> (&data)[N], long long f(T)) { 188 fesetenv(FE_DFL_ENV); 189 for (size_t i = 0; i < N; ++i) { 190 EXPECT_EQ(data[i].expected, f(data[i].input)) << "Failed on element " << i; 191 } 192 } 193 194 // Runs through the array 'data' applying 'f' to each of the pairs of input values 195 // and asserting that the result is within ULP ulps of the expected value. 196 // For testing a (double, double) -> double function like pow(3). 197 template <size_t ULP, typename RT, typename T1, typename T2, size_t N> 198 void DoMathDataTest(data_1_2_t<RT, T1, T2> (&data)[N], RT f(T1, T2)) { 199 fesetenv(FE_DFL_ENV); 200 FpUlpEq<ULP, RT> predicate; 201 for (size_t i = 0; i < N; ++i) { 202 EXPECT_PRED_FORMAT2(predicate, 203 data[i].expected, f(data[i].input1, data[i].input2)) << "Failed on element " << i; 204 } 205 } 206 207 // Runs through the array 'data' applying 'f' to each of the input values 208 // and asserting that the results are within ULP ulps of the expected values. 209 // For testing a (double, double*, double*) -> void function like sincos(3). 210 template <size_t ULP, typename RT1, typename RT2, typename T1, size_t N> 211 void DoMathDataTest(data_2_1_t<RT1, RT2, T1> (&data)[N], void f(T1, RT1*, RT2*)) { 212 fesetenv(FE_DFL_ENV); 213 FpUlpEq<ULP, RT1> predicate1; 214 FpUlpEq<ULP, RT2> predicate2; 215 for (size_t i = 0; i < N; ++i) { 216 RT1 out1; 217 RT2 out2; 218 f(data[i].input, &out1, &out2); 219 EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i; 220 EXPECT_PRED_FORMAT2(predicate2, data[i].expected2, out2) << "Failed on element " << i; 221 } 222 } 223 224 // Runs through the array 'data' applying 'f' to each of the input values 225 // and asserting that the results are within ULP ulps of the expected values. 226 // For testing a (double, double*) -> double function like modf(3). 227 template <size_t ULP, typename RT1, typename RT2, typename T1, size_t N> 228 void DoMathDataTest(data_2_1_t<RT1, RT2, T1> (&data)[N], RT1 f(T1, RT2*)) { 229 fesetenv(FE_DFL_ENV); 230 FpUlpEq<ULP, RT1> predicate1; 231 FpUlpEq<ULP, RT2> predicate2; 232 for (size_t i = 0; i < N; ++i) { 233 RT1 out1; 234 RT2 out2; 235 out1 = f(data[i].input, &out2); 236 EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i; 237 EXPECT_PRED_FORMAT2(predicate2, data[i].expected2, out2) << "Failed on element " << i; 238 } 239 } 240 241 // Runs through the array 'data' applying 'f' to each of the input values 242 // and asserting that the results are within ULP ulps of the expected values. 243 // For testing a (double, int*) -> double function like frexp(3). 244 template <size_t ULP, typename RT1, typename T1, size_t N> 245 void DoMathDataTest(data_1_int_1_t<RT1, T1> (&data)[N], RT1 f(T1, int*)) { 246 fesetenv(FE_DFL_ENV); 247 FpUlpEq<ULP, RT1> predicate1; 248 for (size_t i = 0; i < N; ++i) { 249 RT1 out1; 250 int out2; 251 out1 = f(data[i].input, &out2); 252 EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i; 253 EXPECT_EQ(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, double, int*) -> double function like remquo(3). 260 template <size_t ULP, typename RT1, typename T1, typename T2, size_t N> 261 void DoMathDataTest(data_1_int_2_t<RT1, T1, T2> (&data)[N], RT1 f(T1, T2, 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].input1, data[i].input2, &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 pairs of input values 274 // and asserting that the result is within ULP ulps of the expected value. 275 // For testing a (double, double, double) -> double function like fma(3). 276 template <size_t ULP, typename RT, typename T1, typename T2, typename T3, size_t N> 277 void DoMathDataTest(data_1_3_t<RT, T1, T2, T3> (&data)[N], RT f(T1, T2, T3)) { 278 fesetenv(FE_DFL_ENV); 279 FpUlpEq<ULP, RT> predicate; 280 for (size_t i = 0; i < N; ++i) { 281 EXPECT_PRED_FORMAT2(predicate, 282 data[i].expected, f(data[i].input1, data[i].input2, data[i].input3)) << "Failed on element " << i; 283 } 284 } 285 286