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 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