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
22template <typename RT, typename T1>
23struct data_1_1_t {
24  RT expected;
25  T1 input;
26};
27
28template <typename T1>
29struct data_int_1_t {
30  int expected;
31  T1 input;
32};
33
34template <typename T1>
35struct data_long_1_t {
36  long expected;
37  T1 input;
38};
39
40template <typename T1>
41struct data_llong_1_t {
42  long long expected;
43  T1 input;
44};
45
46template <typename RT, typename T1, typename T2>
47struct data_1_2_t {
48  RT expected;
49  T1 input1;
50  T2 input2;
51};
52
53template <typename RT1, typename RT2, typename T>
54struct data_2_1_t {
55  RT1 expected1;
56  RT2 expected2;
57  T input;
58};
59
60template <typename RT1, typename T>
61struct data_1_int_1_t {
62  RT1 expected1;
63  int expected2;
64  T input;
65};
66
67template <typename RT1, typename T1, typename T2>
68struct data_1_int_2_t {
69  RT1 expected1;
70  int expected2;
71  T1 input1;
72  T2 input2;
73};
74
75template <typename RT, typename T1, typename T2, typename T3>
76struct data_1_3_t {
77  RT expected;
78  T1 input1;
79  T2 input2;
80  T3 input3;
81};
82
83template <typename T> union fp_u;
84
85template <> 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
95template <> 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
106template <> 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
129template <typename T>
130static 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.
142template <typename T>
143size_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
149template <size_t ULP, typename T>
150struct 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).
167template <size_t ULP, typename RT, typename T, size_t N>
168void 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).
180template <size_t ULP, typename T, size_t N>
181void 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).
191template <size_t ULP, typename T, size_t N>
192void 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).
202template <size_t ULP, typename T, size_t N>
203void 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).
213template <size_t ULP, typename RT, typename T1, typename T2, size_t N>
214void 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).
226template <size_t ULP, typename RT1, typename RT2, typename T1, size_t N>
227void 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).
243template <size_t ULP, typename RT1, typename RT2, typename T1, size_t N>
244void 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).
260template <size_t ULP, typename RT1, typename T1, size_t N>
261void 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).
276template <size_t ULP, typename RT1, typename T1, typename T2, size_t N>
277void 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).
292template <size_t ULP, typename RT, typename T1, typename T2, typename T3, size_t N>
293void 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