math_data_test.h revision 1b37ba2178d618221905e17436f38e0c5a8397f3
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
21template <typename RT, typename T1>
22struct data_1_1_t {
23  RT expected;
24  T1 input;
25};
26
27template <typename RT, typename T1, typename T2>
28struct data_1_2_t {
29  RT expected;
30  T1 input1;
31  T2 input2;
32};
33
34template <typename RT1, typename RT2, typename T>
35struct data_2_1_t {
36  RT1 expected1;
37  RT2 expected2;
38  T input;
39};
40
41template <typename T> union fp_u;
42
43template <> union fp_u<float> {
44  float value;
45  struct {
46    unsigned frac:23;
47    unsigned exp:8;
48    unsigned sign:1;
49  } bits;
50  uint32_t sign_magnitude;
51};
52
53template <> union fp_u<double> {
54  double value;
55  struct {
56    unsigned fracl;
57    unsigned frach:20;
58    unsigned exp:11;
59    unsigned sign:1;
60  } bits;
61  uint64_t sign_magnitude;
62};
63
64// TODO: long double.
65
66template <typename T>
67static inline auto SignAndMagnitudeToBiased(const T& value) -> decltype(fp_u<T>::sign_magnitude) {
68  fp_u<T> u;
69  u.value = value;
70  if (u.bits.sign) {
71    return ~u.sign_magnitude + 1;
72  } else {
73    u.bits.sign = 1;
74    return u.sign_magnitude;
75  }
76}
77
78// Based on the existing googletest implementation, which uses a fixed 4 ulp bound.
79template <typename T>
80size_t UlpDistance(T lhs, T rhs) {
81  const auto biased1 = SignAndMagnitudeToBiased(lhs);
82  const auto biased2 = SignAndMagnitudeToBiased(rhs);
83  return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
84}
85
86template <size_t ULP, typename T>
87struct FpUlpEq {
88  ::testing::AssertionResult operator()(const char* /* expected_expression */,
89                                        const char* /* actual_expression */,
90                                        T expected,
91                                        T actual) {
92    if (!isnan(expected) && !isnan(actual) && UlpDistance(expected, actual) <= ULP) {
93      return ::testing::AssertionSuccess();
94    }
95
96    // Output the actual and expected values as hex floating point.
97    char expected_str[64];
98    char actual_str[64];
99    snprintf(expected_str, sizeof(expected_str), "%a", expected);
100    snprintf(actual_str, sizeof(actual_str), "%a", actual);
101
102    return ::testing::AssertionFailure()
103        << "expected (" << expected_str << ") != actual (" << actual_str << ")";
104  }
105};
106
107// Runs through the array 'data' applying 'f' to each of the input values
108// and asserting that the result is within ULP ulps of the expected value.
109// For testing a (double) -> double function like sin(3).
110template <size_t ULP, typename RT, typename T, size_t N>
111void DoMathDataTest(data_1_1_t<RT, T> (&data)[N], RT f(T)) {
112  fesetenv(FE_DFL_ENV);
113  FpUlpEq<ULP, RT> predicate;
114  for (size_t i = 0; i < N; ++i) {
115    EXPECT_PRED_FORMAT2(predicate,
116                        data[i].expected, f(data[i].input)) << "Failed on element " << i;
117  }
118}
119
120// Runs through the array 'data' applying 'f' to each of the pairs of input values
121// and asserting that the result is within ULP ulps of the expected value.
122// For testing a (double, double) -> double function like pow(3).
123template <size_t ULP, typename RT, typename T1, typename T2, size_t N>
124void DoMathDataTest(data_1_2_t<RT, T1, T2> (&data)[N], RT f(T1, T2)) {
125  fesetenv(FE_DFL_ENV);
126  FpUlpEq<ULP, RT> predicate;
127  for (size_t i = 0; i < N; ++i) {
128    EXPECT_PRED_FORMAT2(predicate,
129                        data[i].expected, f(data[i].input1, data[i].input2)) << "Failed on element " << i;
130  }
131}
132
133// Runs through the array 'data' applying 'f' to each of the input values
134// and asserting that the results are within ULP ulps of the expected values.
135// For testing a (double, double*, double*) -> void function like sincos(3).
136template <size_t ULP, typename RT1, typename RT2, typename T1, size_t N>
137void DoMathDataTest(data_2_1_t<RT1, RT2, T1> (&data)[N], void f(T1, RT1*, RT2*)) {
138  fesetenv(FE_DFL_ENV);
139  FpUlpEq<ULP, RT1> predicate1;
140  FpUlpEq<ULP, RT2> predicate2;
141  for (size_t i = 0; i < N; ++i) {
142    RT1 out1;
143    RT2 out2;
144    f(data[i].input, &out1, &out2);
145    EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i;
146    EXPECT_PRED_FORMAT2(predicate2, data[i].expected2, out2) << "Failed on element " << i;
147  }
148}
149