blob: f3d25ef21eb27e811b8b1cd2aecd71eecb32816f [file] [log] [blame]
Elliott Hughes1b37ba22014-11-03 17:03:20 -08001/*
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}