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