Home | History | Annotate | Download | only in tests
      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