Skip to content

Commit c9e2349

Browse files
authored
Merge pull request #1145 from cppalliance/1137
Handle Git issue 1137
2 parents cc0ad12 + 9ba4780 commit c9e2349

File tree

2 files changed

+143
-21
lines changed

2 files changed

+143
-21
lines changed

include/boost/decimal/detail/cmath/asin.hpp

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// Copyright 2024 Matt Borland
1+
// Copyright 2024 - 2025 Matt Borland
2+
// Copyright 2024 - 2025 Christopher Kormanyos
23
// Distributed under the Boost Software License, Version 1.0.
34
// https://www.boost.org/LICENSE_1_0.txt
45

@@ -41,29 +42,42 @@ constexpr auto asin_impl(const T x) noexcept
4142
return x;
4243
}
4344

44-
const auto absx {fabs(x)};
45-
T result {};
45+
const auto absx { fabs(x) };
4646

47-
if (absx <= std::numeric_limits<T>::epsilon())
47+
T result { };
48+
49+
constexpr T cbrt_eps { cbrt(std::numeric_limits<T>::epsilon()) };
50+
51+
constexpr T one { 1 };
52+
53+
if (absx <= cbrt_eps)
4854
{
49-
result = absx;
55+
result = absx * (one + (absx / 6) * absx);
5056
}
51-
else if (absx <= T{5, -1})
57+
else if (absx <= T { 5, -1 })
5258
{
5359
result = asin_series(absx);
5460
}
55-
else if (absx <= T{1, 0})
56-
{
57-
constexpr T half_pi {numbers::pi_v<T> / 2};
58-
result = half_pi - 2 * asin_series(sqrt((1 - absx) / 2));
59-
}
6061
else
6162
{
62-
#ifndef BOOST_DECIMAL_FAST_MATH
63-
result = std::numeric_limits<T>::quiet_NaN();
64-
#else
65-
result = T{0};
66-
#endif
63+
constexpr T half_pi { numbers::pi_v<T> / 2 };
64+
65+
if (absx < one)
66+
{
67+
result = half_pi - 2 * asin_series(sqrt((1 - absx) / 2));
68+
}
69+
else if (absx > one)
70+
{
71+
#ifndef BOOST_DECIMAL_FAST_MATH
72+
result = std::numeric_limits<T>::quiet_NaN();
73+
#else
74+
result = T{0};
75+
#endif
76+
}
77+
else
78+
{
79+
result = half_pi;
80+
}
6781
}
6882

6983
// arcsin(-x) == -arcsin(x)

test/test_asin.cpp

Lines changed: 113 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// Copyright 2024 Matt Borland
1+
// Copyright 2024 - 2025 Matt Borland
2+
// Copyright 2024 - 2025 Christopher Kormanyos
23
// Distributed under the Boost Software License, Version 1.0.
34
// https://www.boost.org/LICENSE_1_0.txt
45

@@ -14,7 +15,7 @@
1415
# pragma clang diagnostic ignored "-Wconversion"
1516
# pragma clang diagnostic ignored "-Wsign-conversion"
1617
# pragma clang diagnostic ignored "-Wfloat-equal"
17-
# if __clang_major__ >= 20
18+
# if (__clang_major__ >= 20)
1819
# pragma clang diagnostic ignored "-Wfortify-source"
1920
# endif
2021
#elif defined(__GNUC__)
@@ -28,9 +29,9 @@
2829

2930
#include <boost/math/special_functions/next.hpp>
3031
#include <boost/core/lightweight_test.hpp>
31-
#include <iostream>
32-
#include <random>
32+
3333
#include <cmath>
34+
#include <random>
3435

3536
#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH)
3637
static constexpr auto N = static_cast<std::size_t>(128U); // Number of trials
@@ -42,6 +43,9 @@ static std::mt19937_64 rng(42);
4243

4344
using namespace boost::decimal;
4445

46+
template<typename T> auto my_zero() -> T;
47+
template<typename T> auto my_one () -> T;
48+
4549
template <typename Dec>
4650
void test_asin()
4751
{
@@ -137,6 +141,100 @@ void print_value(T value, const char* str)
137141
<< "\nExp: " << ptr << "\n" << std::endl;
138142
}
139143

144+
template<typename T>
145+
auto test_asin_edge() -> void
146+
{
147+
using nl = std::numeric_limits<T>;
148+
149+
const T tiny0 { nl::epsilon() * 999 / 1000 };
150+
const T tiny1 { nl::epsilon() };
151+
const T tiny2 { nl::epsilon() * 1000 / 999 };
152+
153+
const T asin_tiny0 { asin(tiny0) };
154+
const T asin_tiny1 { asin(tiny1) };
155+
const T asin_tiny2 { asin(tiny2) };
156+
157+
// tiny1: 1
158+
// tiny2: 1.001001
159+
// tiny0: 0.999
160+
// tiny1: 1
161+
// tiny2: 1.001001001001001
162+
// tiny0: 0.999
163+
// tiny1: 1
164+
// tiny2: 1.001001001001001001001001001001001
165+
166+
constexpr T ctrl_tiny2
167+
{
168+
std::numeric_limits<T>::digits10 < 10 ? T("1.001001")
169+
: std::numeric_limits<T>::digits10 < 20 ? T("1.001001001001001")
170+
: T("1.001001001001001001001001001001001")
171+
};
172+
173+
BOOST_TEST_EQ(asin_tiny0 / nl::epsilon(), T(999, -3));
174+
BOOST_TEST_EQ(asin_tiny1 / nl::epsilon(), T(1));
175+
BOOST_TEST_EQ(asin_tiny2 / nl::epsilon(), ctrl_tiny2);
176+
177+
constexpr T half_pi { numbers::pi_v<T> / 2 };
178+
179+
BOOST_TEST_EQ(asin(my_zero<T>() + my_one<T>()), half_pi);
180+
BOOST_TEST_EQ(asin(my_zero<T>() - my_one<T>()), -half_pi);
181+
}
182+
183+
template<typename T>
184+
void test_asin_1137()
185+
{
186+
using nl = std::numeric_limits<T>;
187+
188+
const T tiny0 { nl::epsilon() * 999/1000 };
189+
const T tiny1 { nl::epsilon() };
190+
const T tiny2 { nl::epsilon() * 1000/999 };
191+
192+
BOOST_TEST(tiny0 != tiny1);
193+
BOOST_TEST(tiny1 != tiny2);
194+
195+
std::stringstream strm { };
196+
197+
BOOST_TEST_EQ(tiny0, asin(tiny0));
198+
BOOST_TEST_EQ(tiny1, asin(tiny1));
199+
BOOST_TEST_EQ(tiny2, asin(tiny2));
200+
201+
const T sqrt_tiny0 { sqrt(nl::epsilon() * 999/1000) };
202+
const T sqrt_tiny1 { sqrt(nl::epsilon()) };
203+
const T sqrt_tiny2 { sqrt(nl::epsilon() * 1000/999) };
204+
205+
BOOST_TEST_EQ(sqrt_tiny0, asin(sqrt_tiny0));
206+
BOOST_TEST_EQ(sqrt_tiny1, asin(sqrt_tiny1));
207+
BOOST_TEST_EQ(sqrt_tiny2, asin(sqrt_tiny2));
208+
209+
const T cbrt_tiny0 { cbrt(nl::epsilon() * 999/1000) };
210+
const T cbrt_tiny1 { cbrt(nl::epsilon()) };
211+
const T cbrt_tiny2 { cbrt(nl::epsilon() * 1000/999) };
212+
const T cbrt_tiny3 { cbrt(nl::epsilon() * 1004/999) };
213+
214+
auto mini_series
215+
{
216+
[](const T eps)
217+
{
218+
return eps * (1 + (eps / 6) * eps);
219+
}
220+
};
221+
222+
auto is_close
223+
{
224+
[](const T a, const T b)
225+
{
226+
const T delta { fabs(a - b) };
227+
228+
return (delta < (std::numeric_limits<T>::epsilon() * 4));
229+
}
230+
};
231+
232+
BOOST_TEST(is_close(asin(cbrt_tiny0), mini_series(cbrt_tiny0)));
233+
BOOST_TEST(is_close(asin(cbrt_tiny1), mini_series(cbrt_tiny1)));
234+
BOOST_TEST(is_close(asin(cbrt_tiny2), mini_series(cbrt_tiny2)));
235+
BOOST_TEST(is_close(asin(cbrt_tiny3), mini_series(cbrt_tiny3)));
236+
}
237+
140238
int main()
141239
{
142240
#ifdef BOOST_DECIMAL_GENERATE_CONSTANT_SIGS
@@ -187,12 +285,22 @@ int main()
187285

188286
test_asin<decimal32_t>();
189287
test_asin<decimal64_t>();
190-
191288
#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH)
192289
test_asin<decimal128_t>();
193290
#endif
194291

195292
test_asin<decimal_fast32_t>();
196293

294+
test_asin_edge<decimal32_t>();
295+
test_asin_edge<decimal64_t>();
296+
test_asin_edge<decimal128_t>();
297+
298+
test_asin_1137<decimal32_t>();
299+
test_asin_1137<decimal64_t>();
300+
test_asin_1137<decimal128_t>();
301+
197302
return boost::report_errors();
198303
}
304+
305+
template<typename T> auto my_zero() -> T { T zero { 0 }; return zero; }
306+
template<typename T> auto my_one () -> T { T one { 1 }; return one; }

0 commit comments

Comments
 (0)