diff options
Diffstat (limited to 'tests/math_data_test.h')
-rw-r--r-- | tests/math_data_test.h | 251 |
1 files changed, 251 insertions, 0 deletions
diff --git a/tests/math_data_test.h b/tests/math_data_test.h new file mode 100644 index 0000000..8aa2bf1 --- /dev/null +++ b/tests/math_data_test.h @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <fenv.h> + +template <typename RT, typename T1> +struct data_1_1_t { + RT expected; + T1 input; +}; + +template <typename T1> +struct data_int_1_t { + int expected; + T1 input; +}; + +template <typename RT, typename T1, typename T2> +struct data_1_2_t { + RT expected; + T1 input1; + T2 input2; +}; + +template <typename RT1, typename RT2, typename T> +struct data_2_1_t { + RT1 expected1; + RT2 expected2; + T input; +}; + +template <typename RT1, typename T> +struct data_1_int_1_t { + RT1 expected1; + int expected2; + T input; +}; + +template <typename RT1, typename T1, typename T2> +struct data_1_int_2_t { + RT1 expected1; + int expected2; + T1 input1; + T2 input2; +}; + +template <typename RT, typename T1, typename T2, typename T3> +struct data_1_3_t { + RT expected; + T1 input1; + T2 input2; + T3 input3; +}; + +template <typename T> union fp_u; + +template <> union fp_u<float> { + float value; + struct { + unsigned frac:23; + unsigned exp:8; + unsigned sign:1; + } bits; + uint32_t sign_magnitude; +}; + +template <> union fp_u<double> { + double value; + struct { + unsigned fracl; + unsigned frach:20; + unsigned exp:11; + unsigned sign:1; + } bits; + uint64_t sign_magnitude; +}; + +// TODO: long double. + +template <typename T> +static inline auto SignAndMagnitudeToBiased(const T& value) -> decltype(fp_u<T>::sign_magnitude) { + fp_u<T> u; + u.value = value; + if (u.bits.sign) { + return ~u.sign_magnitude + 1; + } else { + u.bits.sign = 1; + return u.sign_magnitude; + } +} + +// Based on the existing googletest implementation, which uses a fixed 4 ulp bound. +template <typename T> +size_t UlpDistance(T lhs, T rhs) { + const auto biased1 = SignAndMagnitudeToBiased(lhs); + const auto biased2 = SignAndMagnitudeToBiased(rhs); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); +} + +template <size_t ULP, typename T> +struct FpUlpEq { + ::testing::AssertionResult operator()(const char* /* expected_expression */, + const char* /* actual_expression */, + T expected, + T actual) { + if (!isnan(expected) && !isnan(actual) && UlpDistance(expected, actual) <= ULP) { + return ::testing::AssertionSuccess(); + } + + // Output the actual and expected values as hex floating point. + char expected_str[64]; + char actual_str[64]; + snprintf(expected_str, sizeof(expected_str), "%a", expected); + snprintf(actual_str, sizeof(actual_str), "%a", actual); + + return ::testing::AssertionFailure() + << "expected (" << expected_str << ") != actual (" << actual_str << ")"; + } +}; + +// Runs through the array 'data' applying 'f' to each of the input values +// and asserting that the result is within ULP ulps of the expected value. +// For testing a (double) -> double function like sin(3). +template <size_t ULP, typename RT, typename T, size_t N> +void DoMathDataTest(data_1_1_t<RT, T> (&data)[N], RT f(T)) { + fesetenv(FE_DFL_ENV); + FpUlpEq<ULP, RT> predicate; + for (size_t i = 0; i < N; ++i) { + EXPECT_PRED_FORMAT2(predicate, + data[i].expected, f(data[i].input)) << "Failed on element " << i; + } +} + +// Runs through the array 'data' applying 'f' to each of the input values +// and asserting that the result is within ULP ulps of the expected value. +// For testing a (double) -> int function like ilogb(3). +template <size_t ULP, typename T, size_t N> +void DoMathDataTest(data_int_1_t<T> (&data)[N], int f(T)) { + fesetenv(FE_DFL_ENV); + for (size_t i = 0; i < N; ++i) { + EXPECT_EQ(data[i].expected, f(data[i].input)) << "Failed on element " << i; + } +} + +// Runs through the array 'data' applying 'f' to each of the pairs of input values +// and asserting that the result is within ULP ulps of the expected value. +// For testing a (double, double) -> double function like pow(3). +template <size_t ULP, typename RT, typename T1, typename T2, size_t N> +void DoMathDataTest(data_1_2_t<RT, T1, T2> (&data)[N], RT f(T1, T2)) { + fesetenv(FE_DFL_ENV); + FpUlpEq<ULP, RT> predicate; + for (size_t i = 0; i < N; ++i) { + EXPECT_PRED_FORMAT2(predicate, + data[i].expected, f(data[i].input1, data[i].input2)) << "Failed on element " << i; + } +} + +// Runs through the array 'data' applying 'f' to each of the input values +// and asserting that the results are within ULP ulps of the expected values. +// For testing a (double, double*, double*) -> void function like sincos(3). +template <size_t ULP, typename RT1, typename RT2, typename T1, size_t N> +void DoMathDataTest(data_2_1_t<RT1, RT2, T1> (&data)[N], void f(T1, RT1*, RT2*)) { + fesetenv(FE_DFL_ENV); + FpUlpEq<ULP, RT1> predicate1; + FpUlpEq<ULP, RT2> predicate2; + for (size_t i = 0; i < N; ++i) { + RT1 out1; + RT2 out2; + f(data[i].input, &out1, &out2); + EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i; + EXPECT_PRED_FORMAT2(predicate2, data[i].expected2, out2) << "Failed on element " << i; + } +} + +// Runs through the array 'data' applying 'f' to each of the input values +// and asserting that the results are within ULP ulps of the expected values. +// For testing a (double, double*) -> double function like modf(3). +template <size_t ULP, typename RT1, typename RT2, typename T1, size_t N> +void DoMathDataTest(data_2_1_t<RT1, RT2, T1> (&data)[N], RT1 f(T1, RT2*)) { + fesetenv(FE_DFL_ENV); + FpUlpEq<ULP, RT1> predicate1; + FpUlpEq<ULP, RT2> predicate2; + for (size_t i = 0; i < N; ++i) { + RT1 out1; + RT2 out2; + out1 = f(data[i].input, &out2); + EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i; + EXPECT_PRED_FORMAT2(predicate2, data[i].expected2, out2) << "Failed on element " << i; + } +} + +// Runs through the array 'data' applying 'f' to each of the input values +// and asserting that the results are within ULP ulps of the expected values. +// For testing a (double, int*) -> double function like frexp(3). +template <size_t ULP, typename RT1, typename T1, size_t N> +void DoMathDataTest(data_1_int_1_t<RT1, T1> (&data)[N], RT1 f(T1, int*)) { + fesetenv(FE_DFL_ENV); + FpUlpEq<ULP, RT1> predicate1; + for (size_t i = 0; i < N; ++i) { + RT1 out1; + int out2; + out1 = f(data[i].input, &out2); + EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i; + EXPECT_EQ(data[i].expected2, out2) << "Failed on element " << i; + } +} + +// Runs through the array 'data' applying 'f' to each of the input values +// and asserting that the results are within ULP ulps of the expected values. +// For testing a (double, double, int*) -> double function like remquo(3). +template <size_t ULP, typename RT1, typename T1, typename T2, size_t N> +void DoMathDataTest(data_1_int_2_t<RT1, T1, T2> (&data)[N], RT1 f(T1, T2, int*)) { + fesetenv(FE_DFL_ENV); + FpUlpEq<ULP, RT1> predicate1; + for (size_t i = 0; i < N; ++i) { + RT1 out1; + int out2; + out1 = f(data[i].input1, data[i].input2, &out2); + EXPECT_PRED_FORMAT2(predicate1, data[i].expected1, out1) << "Failed on element " << i; + EXPECT_EQ(data[i].expected2, out2) << "Failed on element " << i; + } +} + +// Runs through the array 'data' applying 'f' to each of the pairs of input values +// and asserting that the result is within ULP ulps of the expected value. +// For testing a (double, double, double) -> double function like fma(3). +template <size_t ULP, typename RT, typename T1, typename T2, typename T3, size_t N> +void DoMathDataTest(data_1_3_t<RT, T1, T2, T3> (&data)[N], RT f(T1, T2, T3)) { + fesetenv(FE_DFL_ENV); + FpUlpEq<ULP, RT> predicate; + for (size_t i = 0; i < N; ++i) { + EXPECT_PRED_FORMAT2(predicate, + data[i].expected, f(data[i].input1, data[i].input2, data[i].input3)) << "Failed on element " << i; + } +} + |