From 1b37ba2178d618221905e17436f38e0c5a8397f3 Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Mon, 3 Nov 2014 17:03:20 -0800 Subject: Improve math tests to allow a specific ulp bound. At the moment our libm is only good enough for a 1 ulp bound on these tests, but that's better than the 4 ulp bound you get from gtest by default. I'm not really happy with the multiple structures and corresponding functions, but at least they mean there's no duplication in the tests themselves, and it should be easy enough for us to make further improvements in future. Change-Id: I004e12970332e1d9531721361d6c34f908cfcecc --- tests/math_data_test.h | 148 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 tests/math_data_test.h (limited to 'tests/math_data_test.h') diff --git a/tests/math_data_test.h b/tests/math_data_test.h new file mode 100644 index 000000000..f3d25ef21 --- /dev/null +++ b/tests/math_data_test.h @@ -0,0 +1,148 @@ +/* + * 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 + +#include + +template +struct data_1_1_t { + RT expected; + T1 input; +}; + +template +struct data_1_2_t { + RT expected; + T1 input1; + T2 input2; +}; + +template +struct data_2_1_t { + RT1 expected1; + RT2 expected2; + T input; +}; + +template union fp_u; + +template <> union fp_u { + float value; + struct { + unsigned frac:23; + unsigned exp:8; + unsigned sign:1; + } bits; + uint32_t sign_magnitude; +}; + +template <> union fp_u { + double value; + struct { + unsigned fracl; + unsigned frach:20; + unsigned exp:11; + unsigned sign:1; + } bits; + uint64_t sign_magnitude; +}; + +// TODO: long double. + +template +static inline auto SignAndMagnitudeToBiased(const T& value) -> decltype(fp_u::sign_magnitude) { + fp_u 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 +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 +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 +void DoMathDataTest(data_1_1_t (&data)[N], RT f(T)) { + fesetenv(FE_DFL_ENV); + FpUlpEq 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 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 +void DoMathDataTest(data_1_2_t (&data)[N], RT f(T1, T2)) { + fesetenv(FE_DFL_ENV); + FpUlpEq 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 +void DoMathDataTest(data_2_1_t (&data)[N], void f(T1, RT1*, RT2*)) { + fesetenv(FE_DFL_ENV); + FpUlpEq predicate1; + FpUlpEq 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; + } +} -- cgit v1.2.3