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