You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
9.5 KiB
300 lines
9.5 KiB
/* |
|
* 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 <math.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 T1> |
|
struct data_long_1_t { |
|
long expected; |
|
T1 input; |
|
}; |
|
|
|
template <typename T1> |
|
struct data_llong_1_t { |
|
long long 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; |
|
}; |
|
|
|
template <> union fp_u<long double> { |
|
long double value; |
|
#if defined(__LP64__) |
|
struct { |
|
unsigned fracl; |
|
unsigned fraclm; |
|
unsigned frachm; |
|
unsigned frach:16; |
|
unsigned exp:15; |
|
unsigned sign:1; |
|
} bits; |
|
__int128_t sign_magnitude; |
|
#else |
|
struct { |
|
unsigned fracl; |
|
unsigned frach:20; |
|
unsigned exp:11; |
|
unsigned sign:1; |
|
} bits; |
|
uint64_t sign_magnitude; |
|
#endif |
|
}; |
|
|
|
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(); |
|
} |
|
|
|
return ::testing::AssertionFailure() |
|
<< "expected (" << std::hexfloat << expected << ") != actual (" << actual << ")"; |
|
} |
|
}; |
|
|
|
// 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 input values |
|
// and asserting that the result is within ULP ulps of the expected value. |
|
// For testing a (double) -> long int function like lrint(3). |
|
template <size_t ULP, typename T, size_t N> |
|
void DoMathDataTest(data_long_1_t<T> (&data)[N], long 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 input values |
|
// and asserting that the result is within ULP ulps of the expected value. |
|
// For testing a (double) -> long long int function like llrint(3). |
|
template <size_t ULP, typename T, size_t N> |
|
void DoMathDataTest(data_llong_1_t<T> (&data)[N], long long 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; |
|
} |
|
}
|
|
|