• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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