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