56b2b2916b
This patch adds more tests for math functions to address coverage issue of math functions discussed in: https://android-review.googlesource.com/#/c/49653/ https://android-review.googlesource.com/#/c/94780/ These are data sets used in regression tests for the Intel the math library (libm). They were collected over a long period of testing various libm implementations. The data sets contain function specific data (special and corner cases such as +/-0, maximum/minimum normalized numbers, +/-infinity, QNaN/SNaN, maximum/minimum denormal numbers, arguments that would produce close to overflow/underflow results, known hard-to-round cases, etc), implementation specific data (arguments close to table look-up values for different polynomial approximations, worst cases for range reduction algorithms) and other data with interesting bit patterns. The reference values are computed with Maple and were converted into hexadecimal format. Change-Id: I7177c282937369eae98f25d02134e4fc3beadde8 Signed-off-by: Jingwei Zhang <jingwei.zhang@intel.com> Signed-off-by: Mingwei Shi <mingwei.shi@intel.com>
252 lines
8.2 KiB
C++
252 lines
8.2 KiB
C++
/*
|
|
* 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;
|
|
}
|
|
}
|
|
|