33#include < algorithm>
44#include < limits>
55#include < random>
6+ #include < vector>
67
8+ #include " base/bundle.hpp"
79#include " boost/multiprecision/cpp_int.hpp"
810#include " functions/multiprecision.hpp"
911#include " glog/logging.h"
2123namespace principia {
2224namespace functions_test {
2325
26+ using namespace principia ::base::_bundle;
2427using namespace boost ::multiprecision;
2528using namespace principia ::functions::_multiprecision;
2629using namespace principia ::numerics::_next;
@@ -30,32 +33,34 @@ namespace sin_cos = principia::numerics::_sin_cos;
3033
3134class SinCosTest : public ::testing::Test {
3235 protected:
36+ struct FunctionStatistics {
37+ cpp_bin_float_50 max_ulps_error = 0 ;
38+ double worst_argument = 0 ;
39+ cpp_bin_float_50 boost_fn_worst_argument = 0 ;
40+ double principia_fn_worst_argument = 0 ;
41+ std::int64_t incorrectly_rounded = 0 ;
42+ };
43+
44+ struct SinCosStatistics {
45+ FunctionStatistics sin;
46+ FunctionStatistics cos;
47+ };
48+
3349 static void SetUpTestCase () {
3450 sin_cos::StaticInitialization ();
3551 }
3652
37- double a_ = 1.0 ;
38-
39- static void RandomArgumentTest ( double const lower_bound ,
40- double const upper_bound ) {
41- std::mt19937_64 random (42 );
53+ template <std:: int64_t iterations_quantum>
54+ static SinCosStatistics RandomArgumentTest ( double const lower_bound,
55+ double const upper_bound ,
56+ std:: int64_t const seed ) {
57+ std::mt19937_64 random (seed );
4258 std::uniform_real_distribution<> uniformly_at (lower_bound, upper_bound);
4359 std::uniform_int_distribution<> uniform_sign (0 , 1 );
4460
45- cpp_bin_float_50 max_sin_ulps_error = 0 ;
46- cpp_bin_float_50 max_cos_ulps_error = 0 ;
47- double worst_sin_argument = 0 ;
48- double worst_cos_argument = 0 ;
49- std::int64_t incorrectly_rounded_sin = 0 ;
50- std::int64_t incorrectly_rounded_cos = 0 ;
51-
52- #if _DEBUG
53- static constexpr std::int64_t iterations = 100 ;
54- #else
55- static constexpr std::int64_t iterations = 250'000 ;
56- #endif
61+ SinCosStatistics s;
5762
58- for (std::int64_t i = 0 ; i < iterations ; ++i) {
63+ for (std::int64_t i = 0 ; i < iterations_quantum ; ++i) {
5964 double const principia_argument =
6065 uniformly_at (random) * ((uniform_sign (random) << 1 ) - 1 );
6166 auto const boost_argument = cpp_rational (principia_argument);
@@ -66,12 +71,14 @@ class SinCosTest : public ::testing::Test {
6671 abs (boost_sin - static_cast <cpp_bin_float_50>(principia_sin));
6772 auto const ulp = NextUp (principia_sin) - principia_sin;
6873 auto const sin_ulps_error = sin_error / ulp;
69- if (sin_ulps_error > max_sin_ulps_error) {
70- max_sin_ulps_error = sin_ulps_error;
71- worst_sin_argument = principia_argument;
74+ if (sin_ulps_error > s.sin .max_ulps_error ) {
75+ s.sin .max_ulps_error = sin_ulps_error;
76+ s.sin .worst_argument = principia_argument;
77+ s.sin .boost_fn_worst_argument = boost_sin;
78+ s.sin .principia_fn_worst_argument = principia_sin;
7279 }
7380 if (sin_ulps_error > 0.5 ) {
74- ++incorrectly_rounded_sin ;
81+ ++s. sin . incorrectly_rounded ;
7582 LOG (ERROR ) << " Sin: " << sin_ulps_error << " ulps at "
7683 << std::setprecision (25 ) << principia_argument;
7784 }
@@ -83,34 +90,91 @@ class SinCosTest : public ::testing::Test {
8390 abs (boost_cos - static_cast <cpp_bin_float_50>(principia_cos));
8491 auto const ulp = NextUp (principia_cos) - principia_cos;
8592 auto const cos_ulps_error = cos_error / ulp;
86- if (cos_ulps_error > max_cos_ulps_error) {
87- max_cos_ulps_error = cos_ulps_error;
88- worst_cos_argument = principia_argument;
93+ if (cos_ulps_error > s.cos .max_ulps_error ) {
94+ s.cos .max_ulps_error = cos_ulps_error;
95+ s.cos .worst_argument = principia_argument;
96+ s.cos .boost_fn_worst_argument = boost_cos;
97+ s.cos .principia_fn_worst_argument = principia_cos;
8998 }
9099 if (cos_ulps_error > 0.5 ) {
91- ++incorrectly_rounded_cos ;
100+ ++s. cos . incorrectly_rounded ;
92101 LOG (ERROR ) << " Cos: " << cos_ulps_error << " ulps at "
93102 << std::setprecision (25 ) << principia_argument;
94103 }
95104 }
96105 }
97106
98- // This implementation is not quite correctly rounded, but not far from it.
99- EXPECT_LE (max_sin_ulps_error, 0.5 );
100- EXPECT_LE (max_cos_ulps_error, 0.5 );
101- EXPECT_EQ (incorrectly_rounded_sin, 0 );
102- EXPECT_EQ (incorrectly_rounded_cos, 0 );
103-
104- LOG (ERROR ) << " Sin error: " << max_sin_ulps_error << std::setprecision (25 )
105- << " ulps for argument: " << worst_sin_argument
106- << " value: " << Sin (worst_sin_argument)
107- << " ; incorrectly rounded: " << std::setprecision (3 )
108- << incorrectly_rounded_sin / static_cast <double >(iterations);
109- LOG (ERROR ) << " Cos error: " << max_cos_ulps_error << std::setprecision (25 )
110- << " ulps for argument: " << worst_cos_argument
111- << " value: " << Cos (worst_cos_argument)
112- << " ; incorrectly rounded: " << std::setprecision (3 )
113- << incorrectly_rounded_cos / static_cast <double >(iterations);
107+ EXPECT_LE (s.sin .max_ulps_error , 0.5 );
108+ EXPECT_LE (s.cos .max_ulps_error , 0.5 );
109+ EXPECT_EQ (s.sin .incorrectly_rounded , 0 );
110+ EXPECT_EQ (s.cos .incorrectly_rounded , 0 );
111+
112+ return s;
113+ }
114+
115+ void ParallelRandomArgumentTest (double const lower_bound,
116+ double const upper_bound) {
117+ #if _DEBUG
118+ static constexpr std::int64_t iterations = 30'000 ;
119+ static constexpr std::int64_t iterations_quantum = 100 ;
120+ #else
121+ static constexpr std::int64_t iterations = 10'000'000 ;
122+ static constexpr std::int64_t iterations_quantum = 10'000 ;
123+ #endif
124+ static_assert (iterations % iterations_quantum == 0 );
125+
126+ Bundle bundle;
127+
128+ std::vector<SinCosStatistics> statistics (iterations / iterations_quantum);
129+ for (std::int64_t i = 0 ; i < statistics.size (); ++i) {
130+ bundle.Add ([i, lower_bound, upper_bound, &statistics]() {
131+ statistics[i] = RandomArgumentTest<iterations_quantum>(
132+ lower_bound, upper_bound, /* seed=*/ i);
133+ return absl::OkStatus ();
134+ });
135+ }
136+ absl::Status const status = bundle.Join ();
137+
138+ SinCosStatistics final_statistics;
139+ for (auto const & s : statistics) {
140+ if (s.sin .max_ulps_error > final_statistics.sin .max_ulps_error ) {
141+ final_statistics.sin .max_ulps_error = s.sin .max_ulps_error ;
142+ final_statistics.sin .worst_argument = s.sin .worst_argument ;
143+ final_statistics.sin .boost_fn_worst_argument =
144+ s.sin .boost_fn_worst_argument ;
145+ final_statistics.sin .principia_fn_worst_argument =
146+ s.sin .principia_fn_worst_argument ;
147+ }
148+ final_statistics.sin .incorrectly_rounded += s.sin .incorrectly_rounded ;
149+ if (s.cos .max_ulps_error > final_statistics.cos .max_ulps_error ) {
150+ final_statistics.cos .max_ulps_error = s.cos .max_ulps_error ;
151+ final_statistics.cos .worst_argument = s.cos .worst_argument ;
152+ final_statistics.cos .boost_fn_worst_argument =
153+ s.cos .boost_fn_worst_argument ;
154+ final_statistics.cos .principia_fn_worst_argument =
155+ s.cos .principia_fn_worst_argument ;
156+ }
157+ final_statistics.cos .incorrectly_rounded += s.cos .incorrectly_rounded ;
158+ }
159+
160+ auto log_statistics = [](std::string_view const fn_name,
161+ FunctionStatistics const & s) {
162+ LOG (ERROR ) << fn_name << " error: " << s.max_ulps_error
163+ << std::setprecision (25 )
164+ << " ulps for argument: " << s.worst_argument << " ("
165+ << std::hexfloat << s.worst_argument << std::defaultfloat
166+ << " ) value: " << s.principia_fn_worst_argument << " ("
167+ << std::hexfloat << s.principia_fn_worst_argument
168+ << std::defaultfloat << " ) vs. expected "
169+ << s.boost_fn_worst_argument << " (" << std::hexfloat
170+ << static_cast <double >(s.boost_fn_worst_argument )
171+ << std::defaultfloat << " ); incorrectly rounded probability: "
172+ << std::setprecision (3 )
173+ << s.incorrectly_rounded / static_cast <double >(iterations);
174+ };
175+
176+ log_statistics (" Sin" , final_statistics.sin );
177+ log_statistics (" Cos" , final_statistics.cos );
114178 }
115179};
116180
@@ -169,19 +233,20 @@ TEST_F(SinCosTest, ReduceIndex) {
169233}
170234
171235TEST_F (SinCosTest, RandomSmall) {
172- RandomArgumentTest (0 , π / 4 );
236+ ParallelRandomArgumentTest (0 , π / 4 );
173237}
174238
175239TEST_F (SinCosTest, RandomTwoTerms) {
176- RandomArgumentTest (π / 4 , 1 << 8 );
240+ ParallelRandomArgumentTest (π / 4 , 1 << 8 );
177241}
178242
179243TEST_F (SinCosTest, RandomThreeTerms) {
180- RandomArgumentTest (1 << 8 , 1 << 18 );
244+ ParallelRandomArgumentTest (1 << 8 , 1 << 18 );
181245}
182246
183247TEST_F (SinCosTest, RandomLarge) {
184- RandomArgumentTest (1 << 18 , std::numeric_limits<double >::max () / 1.0e30 );
248+ ParallelRandomArgumentTest (1 << 18 ,
249+ std::numeric_limits<double >::max () / 1.0e30 );
185250}
186251
187252// Values for which the base algorithm gives an error of 1 ULP.
0 commit comments