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